vue服务端渲染ssr的初步实现

附上Demo的仓库地址:https://gitee.com/jfengz003/vue-ssr-demo.git
希望能与大家一起进步~

一、SSR的概念

传统web开发

在这里插入图片描述

单页应用SPA

请添加图片描述

服务端渲染SSR

请添加图片描述

什么是服务器端渲染 (SSR)?

官方文档:Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。

服务器渲染的 Vue.js 应用程序也可以被认为是"同构"或"通用",因为应用程序的大部分代码都可以在服务器和客户端上运行。

我们从上门解释得到以下结论:
1、Vue SSR是一个在SPA上进行改良的服务端渲染;
2、通过Vue SSR渲染的页面,需要在客户端激活才能实现交互;
3、Vue SSR将包含两部分:服务端渲染的首屏,包含交互的SPA;

为什么使用服务器端渲染 (SSR)?

  • 更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
  • 更快的内容到达时间 (time-to-content),特别是对于缓慢的网络情况或运行缓慢的设备。无需等待所有的 JavaScript 都完成下载并执行,才显示服务器渲染的标记,所以你的用户将会更快速地看到完整渲染的页面。

使用服务器端渲染 (SSR) 时还需要有一些权衡之处:

  • 开发条件所限。浏览器特定的代码,只能在某些生命周期钩子函数 (lifecycle hook) 中使用;一些外部扩展库 (external library) 可能需要特殊处理,才能在服务器渲染应用程序中运行。
  • 涉及构建设置和部署的更多要求。与可以部署在任何静态文件服务器上的完全静态单页面应用程序 (SPA) 不同,服务器渲染应用程序,需要处于 Node.js server 运行环境。
  • 更多的服务器端负载。每个请求都是n个实例的创建,不然会污染,消耗会变得很大

如果你调研服务器端渲染 (SSR) 只是用来改善少数营销页面(例如 /, /about, /contact 等)的 SEO,那么你可能需要预渲染。无需使用 web 服务器实时动态编译 HTML,而是使用预渲染方式,在构建时 (build time) 简单地生成针对特定路由的静态 HTML 文件。优点是设置预渲染更简单,并可以将你的前端作为一个完全静态的站点。

二、如何实现SSR

对于同构开发,我们依然使用webpack打包,我们要解决两个问题:服务端首屏渲染和客户端激活;

这里需要生成一个服务器bundle文件用于服务端首屏渲染和一个客户端bundle文件用于客户端激活
请添加图片描述

新增:app.js、entry-client.js、entry-server.js、server.js

src 
├── router 
├────── index.js # 路由声明 
├── store 
├────── index.js # 全局状态 
├── server
├────── server.js # node服务
├── app.js # 通用入口,⽤于创建vue实例 
├── entry-client.js # 客户端⼊⼝,⽤于静态内容“激活” 
└── entry-server.js # 服务端⼊⼝,⽤于⾸屏内容渲染

修改路由配置

// src/router/index.jsimport Vue from 'vue'
import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [{path: '/',name: 'Login',component: () => import('@/views/login.vue')},{path: '/layout',name: 'Layout',component: () => import('@/views/layout.vue'),redirect: '/layout/pageOne',children: [{path: 'pageOne',name: 'pageOne',component: () => import('@/views/page1.vue')},{path: 'pageTwo',name: 'pageTwo',component: () => import('@/views/page2.vue')}]}
]// 使用工厂函数创建路由
function createRouter() {return new VueRouter({mode: 'history',base: process.env.BASE_URL,routes});
}export default createRouter

通用入口

// src/app.jsimport Vue from 'vue';
import App from './App.vue';
import createRouter from './router';// 使用工厂函数,导出Vue实例和Router实例
export default function createApp() {const router = createRouter();const app = new Vue({router,render: h => h(App)});return { app, router };
}

服务端入口

// src/entry-server.jsimport createApp from "./app";// 返回⼀个函数,接收请求上下⽂,返回创建的vue实例
export default context => {// 这⾥返回⼀个Promise,确保路由或组件准备就绪return new Promise((resolve, reject) => {const { app, router } = createApp();// 进入首屏router.push(context.url);// 路由就绪router.onReady(() => {resolve(app);}, reject);});
}

客户端入口

// entry-client.jsimport createApp from "./app";// 创建vue、router实例
const { app, router } = createApp();
// 路由就绪 执行挂载
router.onReady(() => {app.$mount('#app');
});

webpack配置(vue.config.js)

安装依赖:

npm install webpack-node-externals lodash.merge -D
// vue.config.js// webpack插件
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 path = require('path');// 环境变量:决定入口是客户端还是服务端
const TARGET_NODE = process.env.WEBPACK_TARGET === "node";
const target = TARGET_NODE ? "server" : "client";module.exports = {css: {extract: false},outputDir: path.resolve(__dirname, `./dist/${target}`) ,configureWebpack: () => ({// 将 entry 指向应用程序的 server / client 文件entry: path.resolve(__dirname, `./src/entry-${target}.js`),// 对 bundle renderer 提供 source map 支持devtool: 'source-map',// 这允许 webpack 以 Node 适用方式处理动态导入(dynamic import),// 并且还会在编译 Vue 组件时告知 `vue-loader` 输送面向服务器代码(server-oriented code)。target: TARGET_NODE ? "node" : "web",node: TARGET_NODE ? undefined : false,output: {// 此处告知 server bundle 使用 Node 风格导出模块libraryTarget: TARGET_NODE ? "commonjs2" : undefined},// 外置化应用程序依赖模块。可以使服务器构建速度更快,并生成较小的 bundle 文件。externals: TARGET_NODE? nodeExternals({// 不要外置化 webpack 需要处理的依赖模块。// 可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件,// 你还应该将修改 `global`(例如 polyfill)的依赖模块列入白名单allowlist: [/\.css$/]}): undefined,optimization: {splitChunks: TARGET_NODE ? false : undefined},// 这是将服务器的整个输出构建为单个 JSON 文件的插件。// 服务端默认文件名为 `vue-ssr-server-bundle.json`plugins: [TARGET_NODE ? new VueSSRServerPlugin() : new VueSSRClientPlugin()]}),chainWebpack: config => {config.module.rule("vue").use("vue-loader").tap(options => {merge(options, {optimizeSSR: false});});}
};

对脚本进行配置,安装依赖

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

编写服务端启动脚本

安装依赖:

npm install vue-server-renderer express -S
// server/server.jsconst express = require('express');
const fs = require('fs');
const path = require('path');// 创建express实例和Vue实例
const app = express();// 创建渲染器
const { createBundleRenderer } = require('vue-server-renderer');
// 服务端bundle文件
const serverBundle = require(path.resolve(__dirname, '..', 'dist/server/vue-ssr-server-bundle.json'));
// 客户端清单
const clientManifest = require(path.resolve(__dirname, '..', 'dist/client/vue-ssr-client-manifest.json'));const renderer = createBundleRenderer(serverBundle, {runInNewContext: false,template: fs.readFileSync(path.resolve(__dirname, '..', 'public/index.temp.html'), 'utf-8'), // 宿主模板文件clientManifest
});// 中间件处理静态文件请求
app.use(express.static(path.resolve(__dirname, '..', 'dist/client'), { index: false }));app.get('*', async (req, res) => {try {// 上下文const content = {url: req.url,title: 'ssr test'}const html = await renderer.renderToString(content);console.log('object', html);res.send(html);} catch(err) {res.status(500).send('服务器内部错误');}
});app.listen(3002, () => {console.log('渲染服务器启动成功');
});

新增宿主文件:/public/index.temp.html


ssr

注意:是服务端渲染出口位置,不能有空格

执行打包:

npm run build

cd进入server文件夹,在终端执行:

node server.js

打开http://localhost:3002,查看页面源代码如下图的话,恭喜你,初步实现vue服务端渲染ssr。

请添加图片描述

参考视频:手把手教你打造Vue SSR
参考文章:如何在vue中实现SSR服务端渲染?


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部