Vue SSR demo

vue ssr

创建工程 vue cli3

   vue create ssr

安装依赖

   vue-server-renderer // 渲染器
   express  // nodejs 服务器
   npm i vue-server-renderer express -D

编写服务端脚本

    // nodejs 服务器
    const express = require('express')
    const Vue = require('vue')
    // 创建 express 与vue 实例
    const app = express()
    // 创建渲染实例
    const renderer = require('vue-server-renderer').createRenderer()
    // 将来将Vue 传给渲染器的实例会得到一个html 内容
    const page= new Vue({
        data:{title:'服务端渲染'},
        template:'<div>{{title}},vue ssr</div>'
    })
    app.get('/',async(req,res)=>{
        try {
            const html = await renderer.renderToString(page)
            res.send(html)
        } catch (error) {
            res.status(500).send('服务器内部错误')
        }
    
    })
    app.listen(3000,()=>{
        console.log('渲染服务器启动成功!')
    })

采用vue-router

  1. src下新建一个router文件夹,新建一个index.js
import Vue from 'vue'
import Router from 'vue-router'
import Index from '@/components/Index'
import Detail from '@/components/Detail'

Vue.use(Router)
//将来每次用户请求都需要一个router 实例
export default function createRouter() {
    return new Router({
        mode: 'history',
        routes: [
            { path: '/', component: Index },
            { path: '/detail', component: Detail }
        ]
    })
}
  1. components 下新建两个文件
    Detail.js
<template >
    <div>
        Detail Page
    </div>
</template>
<script>
export default {
    
}
</script>
<style lang='scss' scoped>

</style>

Index.js

<template >
    <div>
        index Page
    </div>
</template>
<script>
export default {
    
}
</script>
<style lang='scss' scoped>

</style>

修改App.vue

<template>
  <div id="app">
   <nav>
     <router-link to='/'>首页</router-link>
     <router-link to='/detail'>详情页</router-link>
   </nav>
   <router-view></router-view>
  </div>
</template>

入口

// 创建vue 实例
import Vue from 'vue'
import App from './App.vue'
import createRouter from './router'
export default function createApp(){
    const router = createRouter()
    const app = new Vue({
        router,
        render:h=>h(App)
    })
    return {app,router}
}

服务端入口

// 渲染首屏幕
import createApp from './app'
export default context=>{
    return Promise((resolve,reject)=>{
        const {app,router} = createApp()
        // 进入首屏
        router.push(context.url)
        router.onReady(()=>{
            resolve(app)
        },reject)
    })
}

客户端入口

// 挂载激活app
import createApp from './app'
const {app,router} = createApp()
router.onReady(()=>{
    app.$mount('#app')
})

webpack 打包
npm i webpack-node-externals lodash.merge -D

const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
const nodeExternals = require("webpack-node-externals")
const merge = require("lodash.merge")
const TARGET_NODE = process.env.WEBPACK_TARGET === 'node'
const target = TARGET_NODE ? "server" :'client'
module.exports = {
    css:{
        extract: false
    },
    outputDir:'./dist/'+target,
    configureWebpack:()=>({
        entry:`./src/entry-${target}.js`,
        devtool:'source-map',
        target: TARGET_NODE ? 'node' : 'web',
        node: TARGET_NODE? undefined:false,
        output:{
            libraryTarget:TARGET_NODE ? 'commonjs2' :undefined
        },
        externals:TARGET_NODE ? nodeExternals({
            allowlist:[/\.css$/]
        }):undefined,
        optimization:{
            splitChunks:TARGET_NODE ? false :undefined
        },
        plugins:[TARGET_NODE? new VueSSRServerPlugin() :new VueSSRClientPlugin()]
    }),
    chainWebpack:config=>{
        config.module
        .rule("vue")
        .use("vue-loader")
        .tap(options=>{
            merge(options,{
                optimizeSSR:false
            });
        });

    }
}

打包脚本
npm i cross-env -D

"scripts": {
    "build:client":"vue-cli-service build",
    "build:server":"cross-env WEBPACK_TARGET=node vue-cli-service build --node server",
    "build":"npm run build:server && npm run build:client"
  },

新建一个template 作为服务端渲染的模版 index.temp.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue ssr</title>
</head>
<body>
    <!--vue-ssr-outlet-->
</body>
</html>

服务端渲染 脚本 更改

// nodejs 服务器
const express = require('express')
const Vue = require('vue')
const fs = require('fs')
// 创建 express 与vue 实例
const app = express()
// 创建渲染实例
const {createBundleRenderer} = require('vue-server-renderer')
// 服务端bundle
const serverBundle = require('../dist/server/vue-ssr-server-bundle.json')
// 客户端清单
const clientManifest = require('../dist/client/vue-ssr-client-manifest.json')
const renderer = createBundleRenderer(serverBundle,{
    runInNewContext:false,
    template:fs.readFileSync('../public/index.temp.html','utf-8'),// 宿主模版文件
    clientManifest
})
// 中间件处理静态文件请求
app.use(express.static('../dist/client',{index:false}))
// 路由的处理交给vue
app.get('*',async(req,res)=>{
    try {
        const context = {
            url:req.url,
            title:'ssr test'
        }
        const html = await renderer.renderToString(context)
        res.send(html)
    } catch (error) {
        res.status(500).send('服务器内部错误')
    }
 
})
app.listen(3000,()=>{
    console.log('渲染服务器启动成功!')
})

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容