>³õѧssrÈë¿Ó
³õѧvue·þÎñ¶ËäÖȾÒÉ»ó·Ç³£¶à£¬ÎÒÃǴ󲿷ÖÇ°¶Ë¶¼ÊÇ°ë·³ö¼Ò£¬ÉÏÊÖ¶¼ÊÇÇ°ºó¶Ë·ÖÀ룬¶Ô·þÎñ¶Ë²¢²»Á˽⣬²»Ëµjava¡¢phpÓïÑÔÁË£¬Á¬node·þÎñ¶¼»¹Ã»¸ãÃ÷°×£¬Àí½â·þÎñ¶ËäÖȾ»¹ÊÇÓÐЩÀ§Äѵģ»
ÍøÉÏÓзdz£¶àµÄvue·þÎñäÖȾµÄÈëÃÅ°¸Àý£¬µ«¿´Á˺ܾ㬺ܶ࣬»¹ÊÇһͷÎíË®£¬¸ã²»Ã÷°×ÕâЩÎļþºÍ¹Ø¼ü×ÖµÄÁªÏµºÍÒâ˼£º
- server.js
- entrt-client.js
- server-js
- built-server-bundle.js
- vue-ssr-server-bundle.json
- vue-ssrclientmanifest.json
- createBundleRenderer
- clientManifest
ÕâƪÄÚÈݻᰴÕÕ »ù´¡·þÎñ¶ËäÖȾ--vueʵÀýäÖȾ--¼ÓÈëvueRouter--¼ÓÈëvueXµÄ˳ÐòÈë¿Ó£¬ºóÐøÓ¦¸Ã»¹ÓÐ--¿ª·¢Ä£Ê½--seoÓÅ»¯--²¿·ÖäÖȾ£¬ÕâÀïÏȲ»ÍÚÄÇô¶à¿ÓÁË£»
>»ù´¡·þÎñ¶ËäÖȾ
¹ËÃû˼Ò壬µÃÆô¸ö·þÎñ£º(½¨¸öÐÂÏîÄ¿£¬²»ÒªÓÃvue-cli)
//server.js const express = require('express'); const chalk = require('chalk');//¼Ó¸öchalk¾ÍÊÇconsoleºÃ¿´µã¡£¡£ const server = express(); server.get('*', (req, res) => { res.set('content-type', "text/html"); res.end(` <!DOCTYPE html> <html lang="en"> <head><title>Hello</title></head> <body>ÄãºÃ</body> </html> `) }) server.listen(8080,function(){ let ip = getIPAdress(); console.log(`·þÎñÆ÷¿ªÔÚ£ºhttp://${chalk.green(ip)}:${chalk.yellow(8080)}`) }) function getIPAdress(){//nodeϵÄosÄ£¿é¿ÉÒÔÄõ½Æô¶¯¸ÃÎļþµÄ·þÎñ¶ËµÄ²¿·ÖÐÅÏ¢ var interfaces = require('os').networkInterfaces(); for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = 0; i < iface.length; i++) { var alias = iface[i]; if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { return alias.address; } } } }
Æô¶¯ node server.js
ÔÙ¿´Ò³Ãæ Õý³££¬Õâ¾ÍÊÇ×î»ù´¡µÄ·þÎñ¶ËäÖȾ
Æäʵ¾ÍÊÇÒ»¸ögetÇëÇ󣬷µ»ØÒ»¸ö×Ö·û´®£¬ä¯ÀÀÆ÷ĬÈÏչʾ·µ»Ø½á¹û£»
È»¶ø¶ÔÓÚÕâ¸ö×Ö·û´®µÄ½âÎö»¹²»Ã÷È·£¬Ê²Ã´Òâ˼£¬±ÈÈ磺
È¥µôÕâ¾ä»°£¬Ò³Ãæ¾Í³ÉÁËÕâÑù£¬ÔÒò²»É£¬×Ô¼º°Ù¶È
>¼ÓÈëvueʵÀý
Ìø¹ý¹ÙÍø˵µÄbuilt-server-bundle.jsÓ¦Óã¬Òâ˼¾ÍÊDz»ÓùÜÕâ¸öÎļþÁË£¬Ö»ÊÇÒ»¸ö¹ý¶ÉÎļþ£¬ÏîÄ¿ÖÐÒ²²»»áÓõ½¡£Ö±½ÓʹÓÃcreateBundleRenderer·½·¨£¬Ö±½ÓÓÃvue-ssr-server-bundle.json£»
¿´ÏÂÏÖÔÚµÄĿ¼½á¹¹£º
ÐÂÔöÁË5¸öÎļþ£»Óйؿͻ§¶ËµÄÅäÖÃentry-client.js²»ÊDZØÐëµÄ£¬ÕâÀïÏȲ»¹Ü£»
app.jsÊÇÓÃÀ´´´½¨vueʵÀýµÄ£»
entry-server.jsÊÇÓÃÀ´´´½¨Éú³Évue-ssr-server-bundle.json£¨ÐèÒªÓõ½app.js£©ËùÐèµÄÅäÖÃÅä¼þ£»ÊǸøwebpack.server.config.jsÓõģ»
webpack.server.config.jsÊÇÓÃÀ´Éú³Évue-ssr-server-bundle.jsonµÄ£»
vue-ssr-server-bundle.jsonÊǸøserver.jsÖеÄcreateBundleRendererÓõġ£
//app.js import Vue from 'vue' import Vue from './App.vue'//ÕâÀïÒ»¶¨ÒªÐ´ÉÏ.vue,²»È»»áÆ¥Åäµ½app.js,require²»Çø·Ö´óСд0.0 export default createApp=function(){ return new Vue({ render:h => h(App) }) }
Ò»¸öcreateAppÉú³ÉÒ»¸övueʵÀý£»
//App.vue <template> <div id='app'> ÕâÊǸöapp </div> </template> <script> export default {} </script>
»¹Ã»Óõ½<router-view>
//weback-base.config.js const path = require('path') const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { output:{ path:path.resolve(__dirname,'./dist'), filename:'build.js', }, module: { rules: [ { test:/\.js$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } }, exclude:[/node_modules/,/assets/] }, { test:/\.vue$/, use:['vue-loader'] } ] }, resolve: { alias:{ '@':path.resolve(__dirname,'../') }, extensions:['.js','.vue','.json'] }, plugins:[ new VueLoaderPlugin() ] }
ÓйØwebpackÅäÖò»†ªàÂ
//webpack.server.config.jsÓÃÀ´Éú³Évue-ssr-server-bundle.json const merge = require('webpack-merge') const baseConfig = require('./webpack.base.js') const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') module.exports = merge(baseConfig, { entry: './entry-server.js', // ÕâÔÊÐí webpack ÒÔ Node ÊÊÓ÷½Ê½(Node-appropriate fashion)´¦Àí¶¯Ì¬µ¼Èë(dynamic import)£¬ // ²¢ÇÒ»¹»áÔÚ±àÒë Vue ×é¼þʱ£¬ // ¸æÖª `vue-loader` ÊäËÍÃæÏò·þÎñÆ÷´úÂë(server-oriented code)¡£ target: 'node', // ¶Ô bundle renderer Ìṩ source map Ö§³Ö devtool: 'source-map', // ´Ë´¦¸æÖª server bundle ʹÓà Node ·ç¸ñµ¼³öÄ£¿é(Node-style exports) output: { libraryTarget: 'commonjs2' }, // ÕâÊǽ«·þÎñÆ÷µÄÕû¸öÊä³ö // ¹¹½¨Îªµ¥¸ö JSON ÎļþµÄ²å¼þ¡£ // ĬÈÏÎļþÃûΪ `vue-ssr-server-bundle.json` plugins: [ new VueSSRServerPlugin() ] })
Õâ¸öÅäÖÃÄĶ¼ÄÜÕÒµ½£¬ÖصãÊÇVueSSRServerPluginÕâ¸ö²å¼þ£¬Éú³Évue-ssr-server-bundle.jsonÈ«¿¿Ëü£¬È¥µôµÄ»°Éú³ÉµÄÊÇbuilt-server-bundle.js£»¹ØÓÚmerge²å¼þ£¬libraryTarget,targetÅäÖÃÎÊÌâ×Ô¼º°Ù¶ÈwebpackÈ¥0.0£»
//entry-server.js import { createApp } from './src/app' export default context => { return createApp() }
¹Ì¶¨Ð´·¨£¬·µ»ØÒ»¸öº¯Êý¹©createBundleRendererʹÓã»
Éú³Évue-ssr-server-bundle.json
µ½Ä¿Ç°ÎªÖ¹°²×°µÄ²å¼þÓУº
×Ô¼ºÊÖ¶¯Ò»¸öÒ»¸ö×°¾ÍÐÐÁË¡£
Éú³Évue-ssr-server-bundle.json£¬Ê¹ÓÃwebpackÃüÁî
Ò»Çж¼ÊÖ¶¯£¬ÊìϤwebpack;
ÐÞ¸Äserver.js
const express = require('express'); const chalk = require('chalk'); const server = express(); const serverBundle = require('./dist/vue-ssr-server-bundle.json')//**ÐÂÔö**// const renderer = require('vue-server-renderer').createBundleRenderer(serverBundle,{ runInNewContext: false, // ¿´Ãû×ÖÒ²ÖªµÀÊÇÉú³Éij¸öеÄContext¶ÔÏó,ĬÈÏÊÇtrue,¸Ä³ÉfalseÀí½âΪijÖÖ»º´æ»úÖÆ£¬Ìá¸ß·þÎñÆ÷ЧÂÊ template: require('fs').readFileSync('./index.html', 'utf-8'), })//**ÐÂÔö**// server.get('*', (req, res) => { //res.set('content-type', "text/html"); //res.end(` //<!DOCTYPE html> //<html lang="en"> // <head><title>Hello</title></head> // <body > // <div style='color:red'>ÄãºÃ</div> // </body> // </html> //¸Ä³ÉÏÂÃæÕâÑù const context = {//ÕâÀïµÄ²ÎÊýÏÖÔÚ»¹Ã»Ó㬵«Õâ¸ö¶ÔÏó»¹ÊǵÃÓã¬Òª×örenderToStringµÄ²ÎÊý url:req.url } renderer.renderToString(context, (err, html) => { if (err) { res.status(500).end('Internal Server Error') return } else { res.end(html) } }) `) }) server.listen(8080,function(){ let ip = getIPAdress(); console.log(`·þÎñÆ÷¿ªÔÚ£ºhttp://${chalk.green(ip)}:${chalk.yellow(8080)}`) }) function getIPAdress(){//nodeϵÄosÄ£¿é¿ÉÒÔÄõ½Æô¶¯¸ÃÎļþµÄ·þÎñ¶ËµÄ²¿·ÖÐÅÏ¢£¬Ï¸½Ú×Ô¼ºÈ¥nodeÉÏÃæ²é var interfaces = require('os').networkInterfaces(); for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = 0; i < iface.length; i++) { var alias = iface[i]; if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { return alias.address; } } } }
ÊÔÒ»¸ò£ºnode server.js
Õý³££¬¼ýÍ·Ö¸µÄµØ·½¹ÙÍøÓнâÊÍ¡£±ðÍüÁËinde.htmlÖмÓÈëÒ»ÐÐ×¢ÊÍ£º
ºóÐøÐÞ¸Ätitle,metaÍ·²¿¶¼ÊÇͨ¹ýÀàËƵÄ×¢ÊÍ·½Ê½£¬ÔÀí¾ÍÊÇÕýÔòÆ¥ÅäÌæ»»×Ö·û´®-¡£-£»
>¼ÓÈë·ÓÉvue-router
ÐÂÔö¼¸¸öÎļþ
ÐèÒªÐ޸ĵÄÎļþÓУº
App.vue//¼Ó¸örouter-view¾ÍÐÐ
//app.js import Vue from 'vue' import App from './App.vue' import router from './router' export function createApp(){ const app = new Vue({ router, render:h => h(App) }) return {app,router} }
°ÑappʵÀýºÍrouter¶¼Å׳öÈ¥£¬¸øentry-server.jsÓÃ
// entry-server.js import { createApp } from './src/app' export default context => { //ÕâÀïÓÃpromiseµÄÔÒòÓкܶ࣬ÆäÖÐÓÐÒ»¸ö¾ÍÊÇÏÂÃæÕâ¸öonReady·½·¨ÊÇÒì²½µÄ¡£createBundleRendererÖ§³Öpromise return new Promise((resolve, reject) => { const { app, router } = createApp() router.push(context.url) router.onReady(() => {//onReady·½·¨»¹ÓÐgetMatchedComponents·½·¨»¹ÊÇÐèÒªÁ˽âһϠconst matchedComponents = router.getMatchedComponents() if (!matchedComponents.length) { return reject({ code: 404 }) } resolve(app) }, reject) }) }
×îºó¿´Ò»ÏÂrouter.js
//router.js import Vue from 'vue' import VueRouter from 'vue-router' //Ò³ÃæÒªÏÈÉùÃ÷ºóʹÓ㬲»ÒªÎÊΪʲô import home from './pages/home' import store from './pages/store' Vue.use(VueRouter) export default new VueRouter({ mode: 'history', routes:[ {path:'/',name:'home',component:home}, {path:'/store',name:'store',component:store}, ] })
ÔÙ¿´Ò»ÏÂÁ½¸öÒ³ÃæµÄ´úÂ룻
//store.vue <template> <div>this is store</div> </template> <script> export default {} </script>
¸ÄµÄ²î²»¶àÁË£¬ÊÔÒ»¹þ£º
ÖØдò¸ö°üwebpack --config webpack.server.js
Æô¶¯node server
>entry-client.jsÊǸÉɶµÄ
µ½Ä¿Ç°ÎªÖ¹»¹Ã»Óõ½entry-client.js½Ð¿Í»§¶ËÅäÖ㬲»×ż±Ê¹Óã¬ÏÈ×ö¸ö²âÊÔ£¬Ð´µãÂß¼ÊÔÊÔ£º
ÐÞ¸ÄÏÂstore.vue
//store.vue <template> <div @click='run'>{{msg}}</div> </template> <script> export default { data(){ msg:'this is store' }, created(){ this.msg = 'this is created' }, mounted(){ this.msg = 'this is mounted' }, methods: { run(){ alert('this is methods') } } } </script>
¿´Õâ¸öÑù×ÓÒ³Ãæ×îÖÕչʾµÄ½á¹ûÓ¦¸ÃÊÇthis is mounted£¬È»¶ø½á¹ûÊÇÕâÑùµÄ£º
ºÜºÃ½âÊÍ£¬·þÎñ¶Ë¶ÔÓÚ¹³×Óº¯ÊýµÄÀí½âÒ²ÊǺÜÕýÈ·µÄ£¬created»áÔÚÒ³Ãæ·µ»Ø֮ǰִÐУ¬¶ømountedÊÇÔÚvueʵÀý³ÉÐÍÖ®ºóÖ´ÐУ¬¾ÍÊÇÒ³ÃæäÖȾºó£¬Õâ¸öÊÇÒªÔÚ¿Í»§¶Ë²Å»áÖ´ÐУ¬¿ÉÊÇΪʲôҳÃæ³öÀ´ÁËûÓÐÖ´ÐÐmounted£¬¶øÇÒrunµÄµã»÷ʼþûÓÐÉúЧ£»
¿´¿´Ò³Ã棺
Ò»¸öjsÎļþ¶¼Ã»¼ÓÔØ£¬ÔõôִÐÐÂß¼£¬¾ÍÊǸö¾²Ì¬Ò³Ãæ0.0£»
Õâʱºòentry-client.js¾Í³ö³¡ÁË
ÐÂÔöÁ½¸öÎļþ
//entry-client.js import { createApp } from './src/app.js'; const { app } = createApp(); app.$mount('#app');
»ù±¾ÅäÖã»
//webpack.client.config.js const merge = require('webpack-merge') const baseConfig = require('./webpack.base.config.js') const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') module.exports = merge(baseConfig, { entry: './entry-client.js', optimization:{ runtimeChunk:true }, plugins: [ // ´Ë²å¼þÔÚÊä³öĿ¼ÖÐ // Éú³É `vue-ssr-client-manifest.json`¡£ new VueSSRClientPlugin(), ] })
Õâ¸öµØ·½Öصã³ýÁËVueSSRClientPluginÉú³Évue-ssr-client-manifest.jsonÍ⣬optimizationÊÇwebpack4²úÎÓÃÀ´·ÖÀëÉú³É¹²¹«chunk,ÅäÖû¹Ë㸴ÔÓ£¬¿ÉÒÔ¿´ÏÂÕâÀïwebpack4 optimization×ܽá
ÐÞ¸ÄÏÂserver.js
//server.js const express = require('express'); const chalk = require('chalk'); const server = express(); const serverBundle = require('./dist/vue-ssr-server-bundle.json') const clientManifest = require('./dist/vue-ssr-client-manifest.json')//ÐÂÔö const renderer = require('vue-server-renderer').createBundleRenderer(serverBundle,{ runInNewContext: false, // ÍƼö template: require('fs').readFileSync('./index.html', 'utf-8'), clientManifest // //ÐÂÔö }) server.get('*', (req, res) => { res.set('content-type', "text/html"); const context = { url:req.url } renderer.renderToString(context, (err, html) => { if (err) { res.status(500).end('Internal Server Error') return } else { res.end(html) } }) }) server.listen(8080,function(){ let ip = getIPAdress(); console.log(`·þÎñÆ÷¿ªÔÚ£ºhttp://${chalk.green(ip)}:${chalk.yellow(8080)}`) }) function getIPAdress(){//nodeϵÄosÄ£¿é¿ÉÒÔÄõ½Æô¶¯¸ÃÎļþµÄ·þÎñ¶ËµÄ²¿·ÖÐÅÏ¢£¬Ï¸½Ú×Ô¼ºÈ¥nodeÉÏÃæ²é var interfaces = require('os').networkInterfaces(); for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = 0; i < iface.length; i++) { var alias = iface[i]; if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { return alias.address; } } } }
´ò°üÏ£ºwebpack --config webpack.client.config.js
node server һϣ¬¿´¿´Ò³Ãæ
jsÓÐÁË£¬¿ÉÊÇΪʲô»¹²»ÐУ¬²»Äܵã0.0£»
¿´¿´¡£°Â±¨´íÁË
¶ÁÈ¡²»µ½¾²Ì¬Îļþ£»
ÐÞ¸Äserver.js¼Ó¸ö¾²Ì¬ÎļþÍйܣº
ÔÙ¿´¿´
ʼþÒ²ÓÐÁË£¬Ò³Ãæû±ä»¯£¬consoleһϣ¬·¢ÏÖÖµÆäʵÒѾ±äÁË£¬Ö»ÊÇʧȥÁËÏìӦʽ£»Õâ¾ÍÊÇΪʲôҪÓÃvuexµÄÔµ¹Ê£»
>¼ÓÈëvuex
¿ªÊ¼ÏëÔÚÒ³ÃæÖÐÓÃthis.$set·½·¨£¬È»¶øÐв»Í¨£¬¶øÇÒ²»¿ÉÄܸøÿ¸öÖµ¶¼ÖØÐÂдһ¸öÕâ¸ö·½·¨£»
¼Ó¸ösotre.js
// store.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { msg: '' }, actions: { setMsg ({ commit }, val) { commit('setMsg', val) } }, mutations: { setMsg (state, val) { Vue.set(state, 'msg', val)//¹Ø¼ü } } })
ºÜ»ù´¡µÄÂß¼£¬¹Ø¼üÔÚVue.setÕâ¸ö·½·¨£¬ÖØÐÂÔö¼ÓÁËÏìӦʽ£»
ÐÞ¸ÄÏÂapp.js
//app.js import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store'//¼Ó¸östore¾ÍÐÐÁË export function createApp(){ const app = new Vue({ router, store, render:h => h(App) }) return {app,router} }
store.vue¸Ä³ÉÕâÑù
<template> <div @click='run'>{{msg}}</div> </template> <script> export default { data(){}, created(){ this.$store.dispatch('setMsg','this is created') }, computed:{ msg(){ return this.$store.state.msg; } }, mounted(){ this.$store.dispatch('setMsg','this is mounted') }, methods: { run(){ alert('this is methods') } } } </script>
ÖØдò¸ö°ü£¬Ïëһϣ¬ÐÞ¸ÄÒ³ÃæµÄ»°Ö»ÐèÒªÖØдò°üclient,Èç¹ûÐÞ¸ÄÁËapp.jsÁ½¸ö¾ÍÒª¶¼ÖØдò°üÁË£»
node server Ò»ÏÂ
Õâ»Ø×ÜËãÍê³ÉÁË£»
>×ܽá
·þÎñ¶ËäÖȾ¶«Î÷»¹ÊÇͦ¶àµÄ£¬Éæ¼°ÁìÓòÒ²·Ç³£¹ã£¬±ÈÈçvue£¬webpack,node£¬ËüÃǵÄÉú̬Ȧ¶¼´óµÄ¿ÉÅ£¬ÐèҪѧϰ¶«Î÷·Ç³£¶à£¬
¿ÓÓֶ࣬ÓÖ´ó£¬ÓÖÉºóÃ滹ÓкܶàÎÊÌâÒª½â¾ö£º
Òì²½Êý¾Ý¼ÓÔØ£»//html·µ»ØÇ°ÏÈäÖȾһ²¿·Ö½Ó¿ÚÄõ½µÄÊý¾Ý Ôõô×öseoÓÅ»¯£»//×ö·þÎñ¶ËäÖȾµÄÖØÒªÔÒò£¬´¦ÀíÒì²½Êý¾Ý¼ÓÔØÎÊÌâÒ²ÊÇΪÁËÕâ¸ö »º´æÔõô¼Ó£» ¿ª·¢»·¾³´î½¨£»//Äã²¢²»Ï£Íûÿ¸ÄÒ»ÐдúÂë¾ÍÖØÐÂÊÖ¶¯´ò¸ö°ü£¬ÖØÆôÏ·þÎñ°É0.0 »¹ÓÐÔõôʵÏÖ²¿·ÖÒ³Ãæssr£»//Ò»¸öÏîÄ¿²»¿ÉÄÜËùÓÐÒ³Ã涼·þÎñ¶ËäÖȾ£¬Ì«ºÄÐÔÄÜ£¬·þÎñÆ÷ѹÁ¦´óѽ£»
»¹ÓкܶàÒÉ»ó£º
±ÈÈçΪʲô»áʧȥÏìӦʽ£¬webpackµ½µ×¸ÃÔõôÅäÖᣡ£
ÒÔÉϾÍÊDZ¾ÎĵÄÈ«²¿ÄÚÈÝ£¬Ï£Íû¶Ô´ó¼ÒµÄѧϰÓÐËù°ïÖú£¬Ò²Ï£Íû´ó¼Ò¶à¶àÖ§³Ö½Å±¾Ö®¼Ò¡£
转载请注明:谷谷点程序 » vue ssr服务端渲染(小白解惑)