美文网首页
Vue SSR demo

Vue SSR demo

作者: 南枫小谨 | 来源:发表于2021-05-21 21:46 被阅读0次

    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('渲染服务器启动成功!')
    })
    
    

    相关文章

      网友评论

          本文标题:Vue SSR demo

          本文链接:https://www.haomeiwen.com/subject/xtsgjltx.html