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 后,会返回 Promisereturn 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 函数,并返回 404if (!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.stateresolve(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 。
推荐阅读
- 又是槐花飘香时阅读启示,槐花飘香阅读理解-
- 4s店|好的服务必定是超前的(4):预见性行动
- 什么人适合学法学?
- 服务器|《糖豆人》免费后匹配系统崩了 E宝自嘲变土豆人
- 服务器|理想L9预订多火爆?网友:第一次见抢46万的车把服务器抢崩溃
- 高通|曝高通骁龙8+下放中端:性能彻底甩开对手
- 服务器|理想L9预定太火爆!服务器直接挤爆了
- 割韭菜是什么意思通俗,割韭菜的韭菜怎么理解-
- 一文理解SpringBean生命周期之PostConstruct、PreDestroy详解
- Mac下的nginx服务器安装本地的https环境
