You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
注意 <!--vue-ssr-outlet--> 注释 -- 这里将是应用程序 HTML 标记注入的地方。
1.4.5 使用模板插值
<html><head><!-- 使用双花括号(double-mustache)进行 HTML 转义插值(HTML-escaped interpolation) --><title>{{ title }}</title><!-- 使用三花括号(triple-mustache)进行 HTML 不转义插值(non-HTML-escaped interpolation) -->
{{{ meta }}}
</head><body><!--vue-ssr-outlet--></body></html>
为了解决这个问题,获取的数据需要位于视图组件之外,即放置在专门的数据预取存储容器(data store)或"状态容器(state container))"中。首先,在服务器端,我们可以在渲染之前预取数据,并将数据填充到 store 中。此外,我们将在 HTML 中序列化(serialize)和内联预置(inline)状态。这样,在挂载(mount)到客户端应用程序之前,可以直接从 store 获取到内联预置(inline)状态。
当使用 template 时,context.state 将作为 window.INITIAL_STATE 状态,自动嵌入到最终的 HTML 中。而在客户端,在挂载到应用程序之前,store 就应该获取到状态:
entry-client.js
importVuefrom'vue'import{createApp}from'./app'const{ app, router, store }=createApp()})if(window.__INITIAL_STATE__){store.replaceState(window.__INITIAL_STATE__)}app.$mount('#app')
1. 服务器端渲染(SSR)简介
1.1 什么是服务器端渲染(SSR)?
1.2 为什么使用服务器端渲染(SSR)?
与传统 SPA(Single-Page Application - 单页应用程序)相比,服务器端渲染(SSR)的优势主要在于:
1.3 使用服务器端渲染(SSR)需要权衡的地方
1.4 一个简单的服务器端渲染
1.4.1 安装依赖
1.4.2 渲染一个 Vue 实例
1.4.3 与服务器集成
1.4.4 使用页面模板
注意
<!--vue-ssr-outlet-->
注释 -- 这里将是应用程序 HTML 标记注入的地方。1.4.5 使用模板插值
通过传入一个"渲染上下文对象",作为 renderToString 函数的第二个参数,来提供插值数据
1.5 服务器端渲染注意事项
2 服务器端渲染构建步骤
我们使用
webpack
来处理服务器和客户端的应用程序,大部分源码可以使用通用方式编写,可以使用webpack
支持的所有功能,一个基本项目可能像是这样:2.1 app.js
app.js
是我们应用程序的「通用 entry」。在纯客户端应用程序中,我们将在此文件中创建根 Vue 实例,并直接挂载到 DOM。但是,对于服务器端渲染(SSR),责任转移到纯客户端 entry 文件。app.js
简单地使用 export 导出一个createApp
函数2.2 entry-client.js
客户端 entry 只需创建应用程序,并且将其挂载到 DOM 中
2.3 entry-server.js
服务器 entry 使用 default export 导出函数,并在每次渲染中重复调用此函数。此时,除了创建和返回应用程序实例之外,还会执行
服务器端路由匹配
和服务器端数据预取
逻辑。对于客户端应用程序和服务器应用程序,我们都要使用 webpack 打包 - 服务器需要「服务器 bundle」然后用于服务器端渲染(SSR),而「客户端 bundle」会发送给浏览器,用于混合静态标记。
3 本文将一步步带您构建一个完整的vue ssr项目
3.1 搭建一个包含交互的简单vue ssr demo
3.1.1 通用代码
components/Comp1.vue
,vue组件App.vue
作为应用程序模板app.js
作为创建根vue实例的工厂entry-server.js
作为服务器端应用程序的入口,每次渲染中重复调用此函数entry-client.js
作为客户端应用程序的入口,将vue实例挂载到dom中3.1.2 构建配置
将配置文件分为三个文件:base, client 和 server。基本配置(base config)包含在两个环境共享的配置,例如,输出路径(output path),别名(alias)和 loader。服务器配置(server config)和客户端配置(client config),可以通过使用 webpack-merge 来简单地扩展基本配置。
webpack.base.config.js
主要包含服务器端应用程序和客户端应用程序打包的共享配置webpack.server.config.js
生成一个库文件,用于创建传递给renderToString
的根vue实例webpack.client.config.js
生成用户激活服务器端静态标记的client bundle3.1.3 执行构建
3.1.4 服务器代码
在分别执行服务器端应用程序构建和客户端应用程序构建之后生成server bundle和client bundle,用于编写服务器代码。
server.js
3.1.5客户端激活
客户端激活,指的是 Vue 在浏览器端接管由服务端发送的静态 HTML,使其变为由 Vue 管理的动态 DOM 的过程。
在客户端入口中,我们通过app.$mount('#app')进行应用程序挂载,由于服务器已经渲染好了 HTML,我们显然无需将其丢弃再重新创建所有的 DOM 元素。相反,我们需要"激活"这些静态的 HTML,然后使他们成为动态的(能够响应后续的数据变化)。
在服务器渲染的HTML里应用程序根元素上有一个特殊的属性——data-server-rendered,该特殊属性能够让客户端Vue知道这部分Html是由Vue在服务器端渲染的,并且应该以激活模式进行挂载。
在开发模式下,Vue 将推断客户端生成的虚拟 DOM 树(virtual DOM tree),是否与从服务器渲染的 DOM 结构(DOM structure)匹配。如果无法匹配,它将退出混合模式,丢弃现有的 DOM 并从头开始渲染。在生产模式下,此检测会被跳过,以避免性能损耗。
3.2 路由和代码分割
我们的服务器代码使用了一个 * 处理程序,它接受任意 URL。这允许我们将访问的 URL 传递到我们的 Vue 应用程序中,然后对客户端和服务器复用相同的路由配置。
使用官方提供的 vue-router,类似于 createApp,我们也需要给每个请求一个新的 router 实例,所以文件导出一个 createRouter 函数
router/index.js
然后更新
app.js
,创建router实例注入到根vue实例中并返回router在
entry-server.js
中实现服务器端路由逻辑(server-side routing logic)router.onReady
该方法把一个回调排队,在路由完成初始导航时调用,这意味着它可以解析所有的异步进入钩子和路由初始化相关联的异步组件。这可以有效确保服务端渲染时服务端和客户端输出的一致。然后再次进行打包构建,服务器只需将请求的路径设置到渲染上下文中
server.js
3.3 数据预取和状态
3.3.1 服务器端数据预取
在服务器端渲染(SSR)期间,我们本质上是在渲染我们应用程序的"快照",所以如果应用程序依赖于一些异步数据,那么在开始渲染过程之前,需要先预取和解析好这些数据。
另一个需要关注的问题是在客户端,在挂载(mount)到客户端应用程序之前,需要获取到与服务器端应用程序完全相同的数据 - 否则,客户端应用程序会因为使用与服务器端应用程序不同的状态,然后导致混合失败。
为了解决这个问题,获取的数据需要位于视图组件之外,即放置在专门的数据预取存储容器(data store)或"状态容器(state container))"中。首先,在服务器端,我们可以在渲染之前预取数据,并将数据填充到 store 中。此外,我们将在 HTML 中序列化(serialize)和内联预置(inline)状态。这样,在挂载(mount)到客户端应用程序之前,可以直接从 store 获取到内联预置(inline)状态。
使用官方提供的状态管理库Vuex,类似于 createApp和createRouter,我们也需要给每个请求一个新的store实例。
store/index.js
然后更新
app.js
,创建store实例注入到根vue实例中并返回store我们需要通过访问路由,来决定获取哪部分数据 - 这也决定了哪些组件需要渲染。事实上,给定路由所需的数据,也是在该路由上渲染组件时所需的数据。所以在路由组件中放置数据预取逻辑,是很自然的事情。
我们将在路由组件上暴露出一个自定义静态函数 asyncData。注意,由于此函数会在组件实例化之前调用,所以它无法访问 this。需要将 store 和路由信息作为参数传递进去:
components/Comp1.vue
在
entry-server.js
中,我们可以通过路由获得与router.getMatchedComponents()
相匹配的组件,如果组件暴露出asyncData
,我们就调用这个方法。然后我们需要将解析完成的状态,附加到渲染上下文(render context)中。entry-server.js
当使用 template 时,context.state 将作为 window.INITIAL_STATE 状态,自动嵌入到最终的 HTML 中。而在客户端,在挂载到应用程序之前,store 就应该获取到状态:
entry-client.js
3.3.2 客户端数据预取
由于服务器端渲染只会进行首屏渲染,后续路由跳转及数据预取则交给客户端应用程序处理。可以在路由导航之前(router.beforeResolve)解析数据,也可以在匹配要渲染的视图后(组件的beforeMount钩子中)再获取数据。
entry-client.js
3.4 利用bundle renderer优化
到目前为止,在每次编辑过应用程序源代码之后,我们都必须重新打包代码并重启服务,这在开发过程中会影响开发效率。此外,Node.js 本身不支持 source map。
vue-server-renderer
提供一个名为createBundleRenderer
的 API,用于处理此问题,通过使用 webpack 的自定义插件,server bundle 将生成为可传递到 bundle renderer 的特殊 JSON 文件。所创建的 bundle renderer,用法和普通 renderer 相同,但是 bundle renderer 提供以下优点:3.4.1 生成server bundle
修改
webpack.server.config.js
,生成传递给createBundleRenderer
的 server bundle。打包后会生成
vue-ssr-server-bundle.json
,传递给createBundleRenderer
。3.4.2 生成 clientManifest
除了 server bundle 之外,我们还可以生成客户端构建清单(client build manifest)。使用客户端清单(client manifest)和服务器 bundle(server bundle),renderer 现在具有了服务器和客户端的构建信息,因此它可以自动推断和注入资源预加载 / 数据预取指令(preload / prefetch directive),以及 css 链接 / script 标签到所渲染的 HTML。
带来的好处:
要使用客户端清单(client manifest),客户端配置(client config)将如下所示:
使用webpack打包构建后会生成vue-ssr-server-bundle.json和vue-ssr-client-manifest.json,服务器就可已使用createBundleRenderer。
3.4.3 开发过程热重载,主要使用
webpack-hot-middleware
和webpack-dev-middleware
这两个webpack插件。具体代码请参考demo5/build/setup-dev-server.js和demo5/server.js。4 总结
The text was updated successfully, but these errors were encountered: