文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Vue服务端如何渲染SSR

2023-07-02 16:38

关注

这篇文章主要介绍“Vue服务端如何渲染SSR”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Vue服务端如何渲染SSR”文章能帮助大家解决问题。

手写Vue服务端渲染

概念:放在浏览器进行就是浏览器渲染,放在服务器进行就是服务器渲染。

Vue服务端如何渲染SSR

一.开始vue-ssr之旅

yarn add vue-server-renderer vueyarn add koa koa-router

createRenderer,创建一个渲染函数 renderToString, 渲染出一个字符串

const Vue = require('vue');const render = require('vue-server-renderer');const Koa = require('koa');const Router = require('koa-router');const app = new Koa();const router = new Router();const vm = new Vue({    data(){        return {msg:"hello world"}    },    template:`<div>{{msg}}</div>`});router.get('/',async (ctx)=>{    let r = await render.createRenderer().renderToString(vm);    ctx.body = `    <!DOCTYPE html>    <html lang="en">    <head>        <meta charset="UTF-8">        <meta name="viewport" content="width=device-width, initial-scale=1.0">        <meta http-equiv="X-UA-Compatible" content="ie=edge">        <title>Document</title>    </head>    <body>        ${r}    </body>    </html>    `});app.use(router.routes());app.listen(4000);

二.采用模板渲染

<!DOCTYPE html><html lang="en">  <head><title>Hello</title></head>  <body>    <!--vue-ssr-outlet-->  </body></html>

传入template 替换掉注释标签

const Vue = require('vue');const render = require('vue-server-renderer');const Koa = require('koa');const Router = require('koa-router');const app = new Koa();const router = new Router();const vm = new Vue({    data(){        return {msg:"hello world"}    },    template:`<div>{{msg}}</div>`});const template = require('fs').readFileSync('./index.html','utf8');router.get('/',async (ctx)=>{    let r = await render.createRenderer({        template    }).renderToString(vm);    ctx.body = r;});app.use(router.routes());app.listen(4000);

三.ssr目录创建

├── config│   ├── webpack.base.js│   ├── webpack.client.js│   └── webpack.server.js├── dist│   ├── client.bundle.js│   ├── index.html│   ├── index.ssr.html│   ├── server.bundle.js│   ├── vue-ssr-client-manifest.json│   └── vue-ssr-server-bundle.json├── package.json├── public│   ├── index.html│   └── index.ssr.html├── server.js├── src│   ├── App.vue│   ├── components│   │   ├── Bar.vue│   │   └── Foo.vue│   ├── entry-client.js│   ├── entry-server.js│   ├── app.js│   ├── router.js│   └── store.js├── webpack.config.js

四.通过webpack实现编译vue项目

安装插件

yarn add webpack webpack-cli webpack-dev-server vue-loader vue-style-loader css-loader html-webpack-plugin @babel/core @babel/preset-env babel-loader vue-template-compiler webpack-merge
const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const VueLoaderPlugin = require('vue-loader/lib/plugin')const resolve = (dir)=>{    return path.resolve(__dirname,dir)}module.exports = {    entry: resolve('./src/client-entry.js'),    output:{        filename:'[name].bundle.js',        path:resolve('dist')    },    module:{        rules:[            {                test:/.css$/,                use:['vue-style-loader','css-loader']            },            {                test:/.js$/,                use:{                    loader:'babel-loader',                    options:{                        presets:['@babel/preset-env']                    }                },                exclude:/node_modules/            },            {                test:/.vue$/,                use:'vue-loader'            }        ]    },    plugins:[        new VueLoaderPlugin(),        new HtmlWebpackPlugin({            template:'./index.html'        })    ]}

app.js

import Vue from "vue";import App from "./App.vue";export default () => { // 为了保证实例的唯一性所以导出一个创建实例的函数  const app = new Vue({    render: h => h(App)  });  return { app };};

client-entry.js

import createApp from "./app";const { app } = createApp();app.$mount("#app"); // 客户端渲染手动挂载到dom元素上

server-entry.js

import createApp from "./app";export default () => {  const { app } = createApp();  return app; // 服务端渲染只需将渲染的实例导出即可};

五.配置客户端打包和服务端打包

let path = require('path');let VueLoaderPlugin = require('vue-loader/lib/plugin')module.exports = {    output:{        filename:'[name].bundle.js',        path:path.resolve(__dirname,'../dist')    },    module:{        rules:[            {test:/.css/,use:['vue-style-loader','css-loader']},            {                test:/.js/,                use:{                    loader:'babel-loader',                    options:{                        presets:['@babel/preset-env']                     },                },                exclude:/node_modules/,            },            {test:/.vue/,use:'vue-loader'}        ]    },    plugins:[        new VueLoaderPlugin()    ]}
const merge = require("webpack-merge");const path = require("path");const HtmlWebpackPlugin = require("html-webpack-plugin");const base = require("./webpack.base");const resolve = filepath => {  return path.resolve(__dirname, filepath);};module.exports = merge(base, {  entry: {    client: resolve("../src/client-entry.js")  },  plugins: [    new HtmlWebpackPlugin({      template: resolve("../template/index.client.html")    })  ]});
const merge = require("webpack-merge");const path = require("path");const HtmlWebpackPlugin = require("html-webpack-plugin");const base = require("./webpack.base");const resolve = filepath => {  return path.resolve(__dirname, filepath);};module.exports = merge(base, {  entry: {    server: resolve("../src/server-entry.js")  },  target: "node",  output: {    libraryTarget: "commonjs2" // 导出供服务端渲染来使用  },  plugins: [    new HtmlWebpackPlugin({      filename: "index.ssr.html",      template: resolve("../template/index.ssr.html"),      excludeChunks: ["server"]    })  ]});

六.配置运行脚本

"scripts": {    "client:dev": "webpack-dev-server --config ./build/webpack.client.js", // 客户端开发环境    "client:build": "webpack --config ./build/webpack.client.js", // 客户端打包环境    "server:build": "webpack --config ./build/webpack.server.js" // 服务端打包环境 },

七.服务端配置

在App.vue上增加id="app"可以保证元素被正常激活

const Koa = require("koa");const Router = require("koa-router");const static = require("koa-static");const path = require("path");const app = new Koa();const router = new Router();const VueServerRenderer = require("vue-server-renderer");const fs = require("fs");// 服务端打包的结果const serverBundle = fs.readFileSync("./dist/server.bundle.js", "utf8");const template = fs.readFileSync("./dist/index.ssr.html", "utf8");const render = VueServerRenderer.createBundleRenderer(serverBundle, {  template});router.get("/", async ctx => {  ctx.body = await new Promise((resolve, reject) => {    render.renderToString((err, html) => {      // 必须写成回调函数的方式否则样式不生效      resolve(html);    });  });});app.use(router.routes());app.use(static(path.resolve(__dirname, "dist")));app.listen(3000);

在index.ssr.html中需要手动引入客户端打包后的结果

七.通过json配置createBundleRenderer方法

实现热更新,自动增加preload和prefetch,以及可以使用sourceMap

const VueSSRClientPlugin = require('vue-server-renderer/client-plugin'); // 在客户端打包时增加插件const VueSSRServerPlugin = require('vue-server-renderer/server-plugin'); // 在服务端打包时增加插件const Koa = require("koa");const Router = require("koa-router");const static = require("koa-static");const path = require("path");const app = new Koa();const router = new Router();const VueServerRenderer = require("vue-server-renderer");const fs = require("fs");// 服务端打包的结果// const serverBundle = fs.readFileSync("./dist/server.bundle.js", "utf8");const template = fs.readFileSync("./dist/index.ssr.html", "utf8");const serverBundle = require("./dist/vue-ssr-server-bundle.json");const clientManifest = require("./dist/vue-ssr-client-manifest.json");const render = VueServerRenderer.createBundleRenderer(serverBundle, {  template,  clientManifest // 自动注入客户端打包后的文件});router.get("/", async ctx => {  ctx.body = await new Promise((resolve, reject) => {    render.renderToString((err, html) => {      // 必须写成回调函数的方式否则样式不生效      resolve(html);    });  });});app.use(router.routes());app.use(static(path.resolve(__dirname, "dist")));app.listen(3000);

八.集成VueRouter

yarn add vue-router
import Vue from "vue";import VueRouter from "vue-router";import Foo from "./components/Foo.vue";Vue.use(VueRouter);export default () => {  const router = new VueRouter({    mode: "history",    routes: [      { path: "/", component: Foo },      { path: "/bar", component: () => import("./components/Bar.vue") }    ]  });  return router;};

导出路由配置

配置入口文件

import Vue from "vue";import App from "./App.vue";import createRouter from "./router";export default () => {  const router = createRouter();  const app = new Vue({    router,    render: h => h(App)  });  return { app, router };};

配置组件信息

<template>    <div id="app">        <router-link to="/"> foo</router-link>        <router-link to="/bar"> bar</router-link>        <router-view></router-view>    </div></template>

防止刷新页面不存在

router.get("*", async ctx => {  ctx.body = await new Promise((resolve, reject) => {    render.renderToString({ url: ctx.url }, (err, html) => {      // 必须写成回调函数的方式否则样式不生效      resolve(html);    });  });});

保证异步路由加载完成

export default ({ url }) => {  return new Promise((resolve, reject) => {    const { app, router } = createApp();    router.push(url);    router.onReady(() => {      const matchComponents = router.getMatchedComponents();      if (!matchComponents.length) {        return reject({ code: 404 });      }      resolve(app);    }, reject);  });};// 服务器可以监控到错误信息,返回404render.renderToString({ url: ctx.url }, (err, html) => {      // 必须写成回调函数的方式否则样式不生效    if (err && err.code == 404) {    resolve("404 Not Found");    }    resolve(html);});

十.集成vuex配置

yarn add vuex
import Vue from 'vue';import Vuex from 'vuex';Vue.use(Vuex);export default ()=>{    let store = new Vuex.Store({        state:{            username:'song'        },        mutations:{            changeName(state){                state.username = 'hello';            }        },        actions:{            changeName({commit}){                return new Promise((resolve,reject)=>{                    setTimeout(() => {                        commit('changeName');                        resolve();                    }, 1000);                })            }        }    });    return store}
// 引用vueximport createRouter from './router';import createStore from './store'export default ()=>{    let router = createRouter();    let store = createStore();    let app = new Vue({        router,        store,        render:(h)=>h(App)    })    return {app,router,store}}

在后端更新vuex

import createApp from './main';export default (context)=>{    return new Promise((resolve)=>{        let {app,router,store} = createApp();        router.push(context.url); // 默认访问到/a就跳转到/a        router.onReady(()=>{            let matchComponents = router.getMatchedComponents(); // 获取路由匹配到的组件                        Promise.all(matchComponents.map(component=>{                if(component.asyncData){                    return component.asyncData(store);                }            })).then(()=>{                context.state = store.state; // 将store挂载在window.__INITIAL_STATE__                resolve(app);            });        })    })}

在浏览器运行时替换store

// 在浏览器运行代码if(typeof window !== 'undefined' && window.__INITIAL_STATE__){    store.replaceState(window.__INITIAL_STATE__);}

需要执行的钩子函数

export default { mounted() {  return this.$store.dispatch("changeName"); }, asyncData(store) {  return store.dispatch("changeName"); }};

关于“Vue服务端如何渲染SSR”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯