vue-ssr技术分析与实践

demo 

https://github.com/flzCoder/rich

一、优劣分析

优点:

更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面

更快的内容到达时间 (time-to-content),特别是对于缓慢的网络情况或运行缓慢的设备

权衡点:

开发条件所限(浏览器特定的代码)

涉及构建设置和部署的更多要求(server端)

更多的服务器端负载(每一个请求生成一个vue实例;在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源 )

nuxt.js

如果你倾向于使用提供了平滑开箱即用体验的更高层次解决方案,你应该去尝试使用 Nuxt.js如果你需要更直接地控制应用程序的结构,Nuxt.js 并不适合这种使用场景。

二、原理分析:

1.核心方法

const { createBundleRenderer } = require('vue-server-renderer')

2.参数:

服务器 bundle: vue-ssr-server-bundle.json

const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

客户端构建清单: vue-ssr-client-manifest.json

const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')

模板index.template.html

 

3.集成到服务器渲染

renderer 现在具有了服务器和客户端的构建信息,因此它可以自动推断和注入资源预加载 / 数据预取指令(preload / prefetch directive),以及 css 链接 / script 标签到所渲染的 HTML。

app.get('*', (req,res) {

renderer.renderToString(context, (err, html) => {
   res.send(html)
})

})

4.简单同构应用已实现

虚拟DOM让服务端渲染vue应用成为可能;

具体实现依赖 vue-server-renderer包的方法和插件,将vue实例渲染为html;

Vue 在浏览器端接管由服务端发送的静态 HTML,使其变为由 Vue 管理的动态 DOM 状态。

三、构建环境搭建:

架构

0.目录结构

1.node应用程序

express

 

2.简易模板

 

3.简易vue应用程序

为每个请求创建一个新的根 Vue 实例,避免状态单例;暴露一个可以重复执行的工厂函数,为每个请求创建新的应用程序实例:

vue应用程序通用entry:app.js

 

4.构建入口文件

entry-client.js

const { app } = createApp()

app.$mount('#app')

entry-server.js

5.构建配置

webpack.client.config.js

webpack.server.config.js

target: 'node',

output.libraryTarget =  'commonjs2';

6.本地开发&线上打包

线上打包

createBundleRenderer 直接调用打包后的文件即可

本地开发

index.html

chokidar监听文件变化,将最新的模板传给createBundleRenderer 

vue-ssr-client-manifest.json

使用express中间件webpack-dev-middleware && webpack-hot-middleware/client,在每次打包结束时将最新的vue-ssr-client-manifest.json传给createBundleRenderer 

vue-ssr-server-bundle.json

watch监听到文件变化,webpack重新打包,新文件vue-ssr-server-bundle.json传给createBundleRenderer 

四、路由相关处理

1.服务器代码使用了一个 * 处理程序,它接受任意 URL。这允许我们将访问的 URL 传递到我们的 Vue 应用程序中,然后对客户端和服务器复用相同的路由配置!

2.路由配置router.js

每个请求一个新的 router 实例,所以文件导出一个 createRouter 函数

应用程序的代码分割或惰性加载,有助于减少浏览器在初始渲染中下载的资源体积,可以极大地改善大体积 bundle 的可交互时间(TTI - time-to-interactive)。

3.路由逻辑

entry-server.js

 

entry-client.js

挂载 app 之前调用 router.onReady,因为路由器必须要提前解析路由配置中的异步组件,才能正确地调用组件中可能存在的路由钩子。

 

五、数据预取及状态

在服务器端渲染(SSR)期间,我们本质上是在渲染我们应用程序的"快照",所以如果应用程序依赖于一些异步数据,那么在开始渲染过程之前,需要先预取和解析好这些数据。

在客户端,在挂载 (mount) 到客户端应用程序之前,需要获取到与服务器端应用程序完全相同的数据 - 否则,客户端应用程序会因为使用与服务器端应用程序不同的状态,然后导致混合失败。

 

1.数据预取存储容器 (vuex)

配置vuex state、action、mutation

action:获取异步数据

2.路由组件所需数据通过访问路由,决定了哪些组件需要渲染。约定在该组件放置数据预取逻辑获取所需数据。

路由组件提供asyncData静态方法,dispatch对应action,获取该路由所需数据存入state.items;

各路由以路由名字区分 route.name

3.服务端数据预取 entry-server.js

通过路由获得与 router.getMatchedComponents() 相匹配的组件,如果组件暴露出 asyncData,我们就调用这个方法。

然后我们需要将解析完成的状态,附加到渲染上下文

当使用 template 时,context.state 将作为 window.__INITIAL_STATE__ 状态,自动嵌入到最终的 HTML 中。而在客户端,在挂载到应用程序之前,store 就应该获取到状态:

4.客户端数据预取 entry-client.js

在路由导航之前解析数据

 使用 `router.beforeResolve()`,以便确保所有异步组件都 resolve。

 
对比得出非预渲染的组件,找出两个匹配列表的差异组件然后预取数据。

当路由组件重用(同一路由,但是 params 或 query 已更改,例如,从 user/1 到 user/2)时,也应该调用 asyncData 函数。我们也可以通过纯客户端 (client-only) 的全局 mixin 来处理这个问题:

 
原文地址:https://www.cnblogs.com/fengluzheweb/p/13570675.html