可以看到,页面的渲染实在便是浏览器将HTML文本转化为页面帧的过程。而如今我们大部分WEB运用都是利用 JavaScript 框架(Vue、React、Angular)进行页面渲染的,也便是说,在实行 JavaScript 脚本的时候,HTML页面已经开始解析并且构建DOM树了,JavaScript 脚本只是动态的改变 DOM 树的构造,使得页面成为希望成为的样子,这种渲染办法叫动态渲染,也可以叫客户端渲染(client side rende)。
那么什么是做事端渲染(server side render)?顾名思义,做事端渲染便是在浏览器要求页面URL的时候,做事端将我们须要的HTML文本组装好,并返回给浏览器,这个HTML文本被浏览器解析之后,不须要经由 JavaScript 脚本的实行,即可直接构建出希望的 DOM 树并展示到页面中。这个做事端组装HTML的过程,叫做做事端渲染。
做事端渲染的由来Web1.0
在没有AJAX的时候,也便是web1.0时期,险些所有运用都是做事端渲染(此时做事器渲染非现在的做事器渲染),那个时候的页面渲染大概是这样的,浏览器要求页面URL,然后做事器吸收到要求之后,到数据库查询数据,将数据丢到后真个组件模板(php、asp、jsp等)中,并渲染成HTML片段,接着做事器在组装这些HTML片段,组成一个完全的HTML,末了返回给浏览器,这个时候,浏览器已经拿到了一个完全的被做事器动态组装出来的HTML文本,然后将HTML渲染到页面中,过程没有任何JavaScript代码的参与。

客户端渲染
在WEB1.0时期,做事端渲染看起来是一个当时的最好的渲染办法,但是随着业务的日益繁芜和后续AJAX的涌现,也逐渐开始暴露出了WEB1.0做事器渲染的缺陷。
每次更新页面的一小的模块,都须要重新要求一次页面,重新查一次数据库,重新组装一次HTML前端JavaScript代码和后端(jsp、php、jsp)代码殽杂在一起,使得日益繁芜的WEB运用难以掩护而且那个时候,根本就没有前端工程师这一职位,前端js的活一样平常都由后端同学 jQuery 一把梭。但是随着前端页面逐渐地繁芜了之后,后端开始创造js好麻烦,虽然很大略,但是坑太多了,于是让公司招聘了一些专门写js的人,也便是前端,这个时候,前后真个鄙视链就涌现了,后端鄙视前端,由于后端以为js太大略,无非便是写写页面的殊效(JS),切切图(CSS),根本算不上是真正的程序员。
随之 nodejs 的涌现,前端看到了翻身的契机,为相识脱后真个指指示点,前端开启了一场前后端分离的运动,希望可以分开后端独立发展。前后端分离,表面上看上去是代码分离,实际上是为了前后端职员分离,也便是前后端分家,前端不再归属于后端团队。
前后端分离之后,网页开始被当成了独立的运用程序(SPA,Single Page Application),前端团队接管了所有页面渲染的事,后端团队只卖力供应所有数据查询与处理的API,大体流程是这样的:首先浏览器要求URL,前端做事器直接返回一个空的静态HTML文件(不须要任何查数据库和模板组装),这个HTML文件中加载了很多渲染页面须要的 JavaScript 脚本和 CSS 样式表,浏览器拿到 HTML 文件后开始加载脚本和样式表,并且实行脚本,这个时候脚本要求后端做事供应的API,获取数据,获取完成后将数据通过JavaScript脚本动态的将数据渲染到页面中,完成页面显示。
这一个前后端分离的渲染模式,也便是客户端渲染(CSR)。
做事端渲染随着单页运用(SPA)的发展,程序员们逐渐创造 SEO(Search Engine Optimazition,即搜索引擎优化)出了问题,而且随着运用的繁芜化,JavaScript 脚本也不断的臃肿起来,使得首屏渲染比较于 Web1.0时候的做事端渲染,也慢了不少。
自己选的路,跪着也要走下去。于是前端团队选择了利用 nodejs 在做事器进行页面的渲染,进而再次涌现了做事端渲染。大体流程与客户端渲染有些相似,首先是浏览器要求URL,前端做事器吸收到URL要求之后,根据不同的URL,前端做事器向后端做事器要求数据,要求完成后,前端做事器会组装一个携带了详细数据的HTML文本,并且返回给浏览器,浏览器得到HTML之后开始渲染页面,同时,浏览器加载并实行 JavaScript 脚本,给页面上的元素绑定事宜,让页面变得可交互,当用户与浏览器页面进行交互,如跳转到下一个页面时,浏览器会实行 JavaScript 脚本,向后端做事器要求数据,获取完数据之后再次实行 JavaScript 代码动态渲染页面。
做事端渲染的利弊
比较于客户端渲染,做事端渲染有什么上风?
利于SEO有利于SEO,实在便是有利于爬虫来爬你的页面,然后在别人利用搜索引擎搜索干系的内容时,你的网页排行能靠得更前,这样你的流量就有越高。那为什么做事端渲染更利于爬虫爬你的页面呢?实在,爬虫也分低级爬虫和高等爬虫。
低级爬虫:只要求URL,URL返回的HTML是什么内容就爬什么内容。高等爬虫:要求URL,加载并实行JavaScript脚本渲染页面,爬JavaScript渲染后的内容。也便是说,低级爬虫对客户端渲染的页面来说,切实其实无能为力,由于返回的HTML是一个空壳,它须要实行 JavaScript 脚本之后才会渲染真正的页面。而目前像百度、谷歌、微软等公司,有一部分年代老旧的爬虫还属于低级爬虫,利用做事端渲染,对这些低级爬虫更加友好一些。
白屏韶光更短相对付客户端渲染,做事端渲染在浏览器要求URL之后已经得到了一个带有数据的HTML文本,浏览器只须要解析HTML,直接构建DOM树就可以。而客户端渲染,须要先得到一个空的HTML页面,这个时候页面已经进入白屏,之后还须要经由加载并实行 JavaScript、要求后端做事器获取数据、JavaScript 渲染页面几个过程才可以看到末了的页面。特殊是在繁芜运用中,由于须要加载 JavaScript 脚本,越是繁芜的运用,须要加载的 JavaScript 脚本就越多、越大,这会导致运用的首屏加载韶光非常长,进而降落了体验感。
做事端渲染缺陷
并不是所有的WEB运用都必须利用SSR,这须要开拓者自己来权衡,由于做事端渲染会带来以下问题:
代码繁芜度增加。为了实现做事端渲染,运用代码中须要兼容做事端和客户端两种运行情形,而一部分依赖的外部扩展库却只能在客户端运行,须要对其进行分外处理,才能在做事器渲染运用程序中运行。须要更多的做事器负载均衡。由于做事器增加了渲染HTML的需求,使得原来只须要输出静态资源文件的nodejs做事,新增了数据获取的IO和渲染HTML的CPU占用,如果流量溘然暴增,有可能导致做事器down机,因此须要利用相应的缓存策略和准备相应的做事器负载。涉及构培植置和支配的更多哀求。与可以支配在任何静态文件做事器上的完备静态单页面运用程序 (SPA) 不同,做事器渲染运用程序,须要处于 Node.js server 运行环境。以是在利用做事端渲染SSR之前,须要开拓者考虑投入产出比,比如大部分运用系统都不须要SEO,而且首屏韶光并没有非常的慢,如果利用SSR反而小题大做了。
同构知道了做事器渲染的利弊后,如果我们须要在项目中利用做事端渲染,我们须要做什么呢?那便是同构我们的项目。
同构的定义在做事端渲染中,有两种页面渲染的办法:
前端做事器通过要求后端做事器获取数据并组装HTML返回给浏览器,浏览器直接解析HTML后渲染页面浏览器在交互过程中,要求新的数据并动态更新渲染页面这两种渲染办法有一个不同点便是,一个是在做事端中组装html的,一个是在客户端中组装html的,运行环境是不一样的。所谓同构,便是让一份代码,既可以在做事端中实行,也可以在客户端中实行,并且实行的效果都是一样的,都是完成这个html的组装,精确的显示页面。也便是说,一份代码,既可以客户端渲染,也可以做事端渲染。
同构的条件为了实现同构,我们须要知足什么条件呢?首先,我们思考一个运用中一个页面的组成,如果我们利用的是Vue.js,当我们打开一个页面时,首先是打开这个页面的URL,这个URL,可以通过运用的路由匹配,找到详细的页面,不同的页面有不同的视图,那么,视图是什么?从运用的角度来看,视图 = 模板 + 数据,那么在 Vue.js 中, 模板可以理解成组件,数据可以理解为数据模型,即相应式数据。以是,对付同构运用来说,我们必须实现客户端与做事真个路由、模型组件、数据模型的共享。
实践
知道了做事端渲染、同构的事理之后,下面从头开始,一步一步完成一次同构,通过实践来理解SSR。
实现根本的NODEJS做事端渲染首先,仿照一个最大略的做事器渲染,只须要向页面返回我们须要的html文件。
const express = require('express');const app = express();app.get('/', function(req, res) { res.send(` <html> <head> <title>SSR</title> </head> <body> <p>hello world</p> </body> </html> `);});app.listen(3001, function() { console.log('listen:3001');});
启动之后打开localhost:3001可以看到页面显示了hello world。而且打开网页源代码:
也便是说,当浏览器拿到做事器返回的这一段HTML源代码的时候,不须要加载任何JavaScript脚本,就可以直接将hello world显示出来。
实现根本的VUE客户端渲染我们用 vue-cli新建一个vue项目,修正一个App.vue组件:
<template> <div> <p>hello world</p> <button @click="sayHello">say hello</button> </div></template><script>export default { methods: { sayHello() { alert('hello ssr'); } }}</script>
然后运行npm run serve启动项目,打开浏览器,一样可以看到页面显示了 hello world,但是打开我们开网页源代码:
除了大略的兼容性处理 noscript 标签以外,只有一个大略的id为app的div标签,没有关于hello world的任何字眼,可以说这是一个空的页面(白屏),而当加载了下面的 script 标签的 JavaScript 脚本之后,页面开始这行这些脚本,实行结束,hello world 正常显示。也便是说真正渲染 hello world 的是 JavaScript 脚本。
同构VUE项目构建配置模板组件的共享,实在便是利用同一套组件代码,为了实现 Vue 组件可以在做事端中运行,首先我们须要办理代码编译问题。一样平常情形,vue项目利用的是webpack进行代码构建,同样,做事端代码的构建,也可以利用webpack,借用官方的一张。
第一步:构建做事端代码
由前面的图可以看到,在做事端代码构建结束后,须要将构建结果运行在nodejs做事器上,但是,对付做事端代码的构建,有一下内容须要把稳:
不须要编译CSS,样式表只有在浏览器(客户端)运行时须要。构建的目标的运行环境是commonjs,nodejs的模块化模式为commonjs不须要代码切割,nodejs将所有代码一次性加载到内存中更有利于运行效率于是,我们得到一个做事真个 webpack 构建配置文件 vue.server.config.js
const nodeExternals = require("webpack-node-externals");const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')module.exports = { css: { extract: false // 不提取 CSS }, configureWebpack: () => ({ entry: `./src/server-entry.js`, // 做事器入口文件 devtool: 'source-map', target: 'node', // 构建目标为nodejs环境 output: { libraryTarget: 'commonjs2' // 构建目标加载模式 commonjs }, // 跳过 node_mdoules,运行时会自动加载,不须要编译 externals: nodeExternals({ allowlist: [/\.css$/] // 许可css文件,方便css module }), optimization: { splitChunks: false // 关闭代码切割 }, plugins: [ new VueSSRServerPlugin() ] })};
利用 vue-server-renderer供应的server-plugin,这个插件紧张合营下面讲到的client-plugin利用,浸染紧张是用来实现nodejs在开拓过程中的热加载、source-map、天生html文件。
第二步:构建客户端代码在构建客户端代码时,利用的是客户真个实行入口文件,构建结束后,将构建结果在浏览器运行即可,但是在做事端渲染中,HTML是由做事端渲染的,也便是说,我们要加载那些JavaScript脚本,是做事端决定的,由于HTML中的script标签是由做事端拼接的,以是在客户端代码构建的时候,我们须要利用插件,天生一个构建结果清单,这个清单是用来见告做事端,当前页面须要加载哪些JS脚本和CSS样式表。
于是我们得到了客户真个构建配置,vue.client.config.js
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')module.exports = { configureWebpack: () => ({ entry: `./src/client-entry.js`, devtool: 'source-map', target: 'web', plugins: [ new VueSSRClientPlugin() ] }), chainWebpack: config => { // 去除所有关于客户端天生的html配置,由于已经交给后端天生 config.plugins.delete('html'); config.plugins.delete('preload'); config.plugins.delete('prefetch'); }};
利用vue-server-renderer供应的client-server,紧张浸染是天生构建加过清单vue-ssr-client-manifest.json,做事端在渲染页面时,根据这个清单来渲染HTML中的script标签(JavaScript)和link标签(CSS)。
接下来,我们须要将vue.client.config.js和vue.server.config.js都交给vue-cli内置的构建配置文件vue.config.js,根据环境变量利用不同的配置
// vue.config.jsconst TARGET_NODE = process.env.WEBPACK_TARGET === 'node';const serverConfig = require('./vue.server.config');const clientConfig = require('./vue.client.config');if (TARGET_NODE) { module.exports = serverConfig;} else { module.exports = clientConfig;}
利用cross-env区分环境
{ "scripts": { "server": "babel-node src/server.js", "serve": "vue-cli-service serve", "build": "vue-cli-service build", "build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build --mode server" }}
模板组件共享第一步:创建VUE实例
为了实现模板组件共享,我们须要将获取 Vue 渲染实例写成通用代码,如下 createApp:
import Vue from 'vue';import App from './App';export default function createApp (context) { const app = new Vue({ render: h => h(App) }); return { app };};
第二步:客户端实例化VUE
新建客户端项目的入口文件,client-entry.js
import Vue from 'vue'import createApp from './createApp';const {app} = createApp();app.$mount('#app');
client-entry.js是浏览器渲染的入口文件,在浏览器加载了客户端编译后的代码后,组件会被渲染到id为app的元素节点上。
第三步:做事端实例化VUE新建做事端代码的入口文件,server-entry.js
import createApp from './createApp'export default context => { const { app } = createApp(context); return app;}
server-entry.js是供应给做事器渲染vue组件的入口文件,在浏览器通过URL访问到做事器后,做事器须要利用server-entry.js供应的函数,将组件渲染成html。
第四步:HTTP做事所有东西的准备好之后,我们须要修正nodejs的HTTP做事器的启动文件。首先,加载做事端代码server-entry.js的webpack构建结果
const path = require('path');const serverBundle = path.resolve(process.cwd(), 'serverDist', 'vue-ssr-server-bundle.json');const {createBundleRenderer} = require('vue-server-renderer');const serverBundle = path.resolve(process.cwd(), 'serverDist', 'vue-ssr-server-bundle.json');
加载客户端代码client-entry.js的webpack构建结果
const clientManifestPath = path.resolve(process.cwd(), 'dist', 'vue-ssr-client-manifest.json');const clientManifest = require(clientManifestPath);
利用 vue-server-renderer 的createBundleRenderer创建一个html渲染器:
const template = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8');const renderer = createBundleRenderer(serverBundle, { template, // 利用HTML模板 clientManifest // 将客户真个构建结果清单传入});
创建HTML模板,index.html
<html> <head> <title>SSR</title> </head> <body> <!--vue-ssr-outlet--> </body></html>
在HTML模板中,通过传入的客户端渲染结果clientManifest,将自动注入所有link样式表标签,而占位符将会被更换成模板组件被渲染后的详细的HTML片段和script脚本标签。
HTML准备完成后,我们在server中挂起所有路由要求
const express = require('express');const app = express();/ code todo 实例化渲染器renderer /app.get('', function(req, res) { renderer.renderToString({}, (err, html) => { if (err) { res.send('500 server error'); return; } res.send(html); })});
接下来,我们构建客户端、做事端项目,然后实行 node server.js,打开页面源代码,
看起来是符合预期的,但是创造掌握台有报错,加载不到客户端构建css和js,报404,缘故原由很明确,我们没有把客户真个构建结果文件挂载到做事器的静态资源目录,在挂载路由前加入下面代码:
app.use(express.static(path.resolve(process.cwd(), 'dist')));
看起来大功告成,点击say hello也弹出了,细心的同学会创造根节点有一个data-server-rendered属性,这个属性有什么浸染呢?
由于做事器已经渲染好了 HTML,我们显然无需将其丢弃再重新创建所有的 DOM 元素。相反,我们须要"激活"这些静态的 HTML,然后使他们成为动态的(能够相应后续的数据变革)。
如果检讨做事器渲染的输出结果,运用程序的根元素上添加了一个分外的属性:
<div id="app" data-server-rendered="true">
data-server-rendered是分外属性,让客户端 Vue 知道这部分 HTML 是由 Vue 在做事端渲染的,并且该当以激活模式进行挂载。
路由的共享和同步完成了模板组件的共享之后,下面完成路由的共享,我们前面做事器利用的路由是,接管任意URL,这许可所有URL要求交给Vue路由处理,进而完成客户端路由与做事端路由的复用。
第一步:创建ROUTER实例为了实现复用,与createApp一样,我们创建一个createRouter.js
import Vue from 'vue';import Router from 'vue-router';import Home from './views/Home';import About from './views/About';Vue.use(Router)const routes = [{ path: '/', name: 'Home', component: Home}, { path: '/about', name: 'About', component: About}];export default function createRouter() { return new Router({ mode: 'history', routes })}
在createApp.js中创建router
import Vue from 'vue';import App from './App';import createRouter from './createRouter';export default function createApp(context) { const router = createRouter(); // 创建 router 实例 const app = new Vue({ router, // 注入 router 到根 Vue 实例 render: h => h(App) }); return { router, app };};
第二步:路由匹配
router准备好了之后,修正server-entry.js,将要求的URL通报给router,使得在创建app的时候可以根据URL匹配到对应的路由,进而可知道须要渲染哪些组件
import createApp from './createApp';export default context => { // 由于有可能会是异步路由钩子函数或组件,以是我们将返回一个 Promise, // 以便做事器能够等待所有的内容在渲染前就已经准备就绪。 return new Promise((resolve, reject) => { const { app, router } = createApp(); // 设置做事器端 router 的位置 router.push(context.url) // onReady 等到 router 将可能的异步组件和钩子函数解析完 router.onReady(() => { const matchedComponents = router.getMatchedComponents(); // 匹配不到的路由,实行 reject 函数,并返回 404 if (!matchedComponents.length) { return reject({ code: 404 }); } // Promise 该当 resolve 运用程序实例,以便它可以渲染 resolve(app) }, reject) })}
修正server.js的路由,把url通报给renderer
app.get('', function(req, res) { const context = { url: req.url }; renderer.renderToString(context, (err, html) => { if (err) { console.log(err); res.send('500 server error'); return; } res.send(html); })});
为了测试,我们将App.vue修正为router-view
<template> <div id="app"> <router-link to="/">Home</router-link> <router-link to="/about">About</router-link> <router-view /> </div></template>
Home.vue
<template> <div>Home Page</div></template>
About.vue
<template> <div>About Page</div></template>
编译,运行,查看源代码
点击路由并没有刷新页面,而是客户端路由跳转的,统统符合预期。
数据模型的共享与状态同步前面我们大略的实现了做事端渲染,但是实际情形下,我们在访问页面的时候,还须要获取须要渲染的数据,并且渲染成HTML,也便是说,在渲染HTML之前,我们须要将所有数据都准备好,然后通报给renderer。
一样平常情形下,在Vue中,我们将状态数据交给Vuex进行管理,当然,状态也可以保存在组件内部,只不过须要组件实例化的时候自己去同步数据。
第一步:创建STORE实例首先第一步,与createApp类似,创建一个createStore.js,用来实例化store,同时供应给客户端和做事端利用
import Vue from 'vue';import Vuex from 'vuex';import {fetchItem} from './api';Vue.use(Vuex);export default function createStore() { return new Vuex.Store({ state: { item: {} }, actions: { fetchItem({ commit }, id) { return fetchItem(id).then(item => { commit('setItem', item); }) } }, mutations: { setItem(state, item) { Vue.set(state.item, item); } } })}
actions封装了要求数据的函数,mutations用来设置状态。
将createStore加入到createApp中,并将store注入到vue实例中,让所有Vue组件可以获取到store实例
export default function createApp(context) { const router = createRouter(); const store = createStore(); const app = new Vue({ router, store, // 注入 store 到根 Vue 实例 render: h => h(App) }); return { router, store, app };};
为了方便测试,我们mock一个远程做事函数fetchItem,用于查询对应item
export function fetchItem(id) { const items = [ { name: 'item1', id: 1 }, { name: 'item2', id: 2 }, { name: 'item3', id: 3 } ]; const item = items.find(i => i.id == id); return Promise.resolve(item);}
第二步:STORE连接组件
一样平常情形下,我们须要通过访问路由,来决定获取哪部分数据,这也决定了哪些组件须要渲染。事实上,给定路由所需的数据,也是在该路由上渲染组件时所需的数据。以是,我们须要在路由的组件中放置数据预取逻辑函数。
在Home组件中自定义一个静态函数asyncData,须要把稳的是,由于此函数会在组件实例化之前调用,以是它无法访问 this。须要将 store 和路由信息作为参数通报进去
<template><div> <div>id: {{item.id}}</div> <div>name: {{item.name}}</div></div></template><script>export default { asyncData({ store, route }) { // 触发 action 后,会返回 Promise return store.dispatch('fetchItems', route.params.id) }, computed: { // 从 store 的 state 工具中的获取 item。 item() { return this.$store.state.item; } }}</script>
第三步:做事端获取数据
在做事器的入口文件server-entry.js中,我们通过URL路由匹配 router.getMatchedComponents()得到了须要渲染的组件,这个时候我们可以调用组件内部的asyncData方法,将所须要的所有数据都获取完后,通报给渲染器renderer高下文。
修正createApp,在路由组件匹配到了之后,调用asyncData方法,获取数据后通报给renderer
import createApp from './createApp';export default context => { // 由于有可能会是异步路由钩子函数或组件,以是我们将返回一个 Promise, // 以便做事器能够等待所有的内容在渲染前就已经准备就绪。 return new Promise((resolve, reject) => { const { app, router, store } = createApp(); // 设置做事器端 router 的位置 router.push(context.url) // onReady 等到 router 将可能的异步组件和钩子函数解析完 router.onReady(() => { const matchedComponents = router.getMatchedComponents(); // 匹配不到的路由,实行 reject 函数,并返回 404 if (!matchedComponents.length) { return reject({ code: 404 }) } // 对所有匹配的路由组件调用 `asyncData()` Promise.all(matchedComponents.map(Component => { if (Component.asyncData) { return Component.asyncData({ store, route: router.currentRoute }); } })).then(() => { // 状态通报给renderer的高下文,方便后面客户端激活数据 context.state = store.state resolve(app) }).catch(reject); }, reject); })}
将state存入context后,在做事端渲染HTML时候,也便是渲染template的时候,context.state会被序列化到window.__INITIAL_STATE__中,方便客户端激活数据。
第四步:客户端激活状态数据做事端预要求数据之后,通过将数据注入到组件中,渲染组件并转化成HTML,然后吐给客户端,那么客户端为了激活后端返回的HTML被解析后的DOM节点,须要将后端渲染组件时用的store的state也同步到浏览器的store中,担保在页面渲染的时候保持与做事器渲染时的数据是同等的,才能完成DOM的激活,也便是我们前面说到的data-server-rendered标记。
在做事真个渲染中,state已经被序列化到了window.__INITIAL_STATE__,比如我们访问http://localhost:3001?id=1,查看页面源代码
可以看到,状态已经被序列化到window.__INITIAL_STATE__中,我们须要做的便是将这个window.__INITIAL_STATE__在客户端渲染之前,同步到客户真个store中,下面修正client-entry.js
const { app, router, store } = createApp();if (window.__INITIAL_STATE__) { // 激活状态数据 store.replaceState(window.__INITIAL_STATE__);}router.onReady(() => { app.$mount('#app', true);});
通过利用store的replaceState函数,将window.__INITIAL_STATE__同步到store内部,完成数据模型的状态同步。
总结当浏览器访问做事端渲染项目时,做事端将URL传给到预选构建好的VUE运用渲染器,渲染器匹配到对应的路由的组件之后,实行我们预先在组件内定义的asyncData方法获取数据,并将获取完的数据通报给渲染器的高下文,利用template组装成HTML,并将HTML和状态state一并吐给前端浏览器,浏览器加载了构建好的客户端VUE运用后,将state数据同步到前真个store中,并根据数据激活后端返回的被浏览器解析为DOM元素的HTML文本,完成了数据状态、路由、组件的同步,同时使得页面得到直出,较少了白屏韶光,有了更好的加载体验,同时更有利于SEO。
个人以为理解做事端渲染,有助于提升前端工程师的综合能力,由于它的内容除了前端框架,还有前端构建和后端内容,是一个性价比还挺高的知识,不学白不学,加油!