>³õѧ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服务端渲染(小白解惑)