这很难,里面只是我以比较明显的一个问题,引发对整个ssr的研究。但是我又复习了下Vuex,发现了异步问题。过两天把router也复习了。那异步问题应该就解决了,到时候再出篇稿子。这篇,你可能看不懂,因为不适合新手,对于context的估计,如果有说的不对的,也请各位专家指教。
1.vue ssr 入门
1.一个简单的例子
接下去,我们使用express来继续,得到的是这个
他和render一样。不过服务器渲染需要两个步奏,第一步,先制作出renderer,第二步是使用renderTOString函数
2.使用页面模板
注意 <!--vue-ssr-outlet-->
注释 – 这里将是应用程序 HTML 标记注入的地方。
插值
也可以与 Vue 应用程序实例共享 context
对象,允许模板插值中的组件动态地注册数据。关于context具体的详情会在后面展开。
3.完整的demo
关于context和html
我们发现经过renderToString的函数方法html已经将三个完美融合
分别是读取html 的renderer,和context,和Vue组件。
之后我们获取了经过加工的context。
显然Vue,要么是读取了html,然后将context加工后的数据渲染到了HTML。
加工点,需要两到三个材料。
2.ssr编写过程注意点
1.服务器上的数据响应
禁用响应式数据,还可以避免将「数据」转换为「响应式对象」的性能开销。
2.组件生命周期钩子函数
由于没有动态更新,所有的生命周期钩子函数中,只有 beforeCreate
和 created
会在服务器端渲染 (SSR) 过程中被调用。
你应该避免在 beforeCreate
和 created
生命周期时产生全局副作用的代码,由于在 SSR 期间并不会调用销毁钩子函数,所以 timer 将永远保留下来。
3.访问特定平台(Platform-Specific) API
4.自定义指令
大多数自定义指令直接操作 DOM,因此会在服务器端渲染 (SSR) 过程中导致错误。有两种方法可以解决这个问题:
- 推荐使用组件作为抽象机制,并运行在「虚拟 DOM 层级(Virtual-DOM level)」(例如,使用渲染函数(render function))。
- 如果你有一个自定义指令,但是不是很容易替换为组件,则可以在创建服务器 renderer 时,使用
directives
选项所提供"服务器端版本(server-side version)"。
export default function show (node: VNodeWithData, dir: VNodeDirective) {
if (!dir.value) {
const style: any = node.data.style || (node.data.style = {})
if (Array.isArray(style)) {
style.push({ display: 'none' })
} else {
style.display = 'none'
}
}
}
这里还需要补充虚拟node
3.关于render 渲染函数的补充
1.渲染函数
首先,我们强调
整个Vue 中的渲染函数有三种方法
1.render
2.template
3.renderer
2.挂载方法
我们再来探讨下挂载的三种方法
1.全局注册
2.局部注册
1.组件和挂载点
2.组件和组件之间
3.模块挂载
这是在单文件中模块挂载的写法
这是vue-cli自带的。
还有一种比较骚,不作为讲解,就以上。我们不难看出,Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。
那么渲染函数本质就是代替template。组件是什么回事。他的数据是先从组件写上去还是,从父组件中取回数据。都有。
但是有一点确定,他一定会渲染上去。
这里context就是不管是父的还是子的,最终的数据都是渲染上去了。
我们就用这个context去寻找,我们所需要的数据。
到这里为止,我们已经知道这东西来自组件。客户端的context是Vue的一个写死的APi。具体的深入还有很多。请仔细查看。至于服务端的context。我们拉开围绕我们整个ssr的一个大boss。
那么开始下一节
4.源码结构
1.避免状态单例
那么context,是否可以解决了。非常糟糕,有些情况,我们不得的以它为假设,然后进行输出。
接下去就是关于server bundle 和bundle renderer 进行讨论。
5.路由和代码分隔
1.使用使用 vue-router
的路由
到目前我们存在了两个问题,结合别人的demo。
1.为什么调用renderTostream,居然会跑到entry-server,我们看到
2.就是这个server.bundle.js在哪里
在例子中server.bundle.js没有生成的。
但是在dist 中没有server.bundle.js,在https://www.webpackjs.com/guides/development/#%E4%BD%BF%E7%94%A8-source-map,我们知道他是不会显示在dist中的。
3.再次探讨context的本质
查看源码结构的第一张图,我们发现这个简单的demo没有entry-server.js 结合上面的Vue官方给出的路由。再结合上面提到的第一个问题。
这两个应该请求是同一个功能的文件。是的,在后者中,我们看到他把context送去给Vue 实例加工。返回一个app。
前者也是一样createApp中主要功能就是new Vue ,进行实例加工。将Vue实例和context进行融合。
从本质上,我们彻底搞清楚了,服务器渲染需要的两大部分。同时我们意识到渲染的时候,已经配置renderer的时候,没有那么简单。
又有新的问题
经过打包的入口变成了
到底发生了什么。
我们进入其中
我们也找到了
我们有理由相信他将这两个混为了一个app.js,所以我们还要对webpack进行研究。、
而且你已经发现了他们两者的不同
在接下去的一段章节,可能不会讲到这,你需要保持专注。
2.代码分隔
1.异步读取
2.onready的作用
需要注意的是,你仍然需要在挂载 app 之前调用 router.onReady
,因为路由器必须要提前解析路由配置中的异步组件,才能正确地调用组件中可能存在的路由钩子。
3.异步组件路由配置
6.数据预取和状态
1.数据预取存储容器 (Data Store)
1.在服务器端渲染(SSR)期间,我们本质上是在渲染我们应用程序的"快照",所以如果应用程序依赖于一些异步数据,那么在开始渲染过程之前,需要先预取和解析好这些数据。
2.首先,在服务器端,我们可以在渲染之前预取数据,并将数据填充到 store 中。此外,我们将在 HTML 中序列化(serialize)和内联预置(inline)状态。这样,在挂载(mount)到客户端应用程序之前,可以直接从 store 获取到内联预置(inline)状态。
2.带有逻辑配置的组件 (Logic Collocation with Components)
3.服务器端数据预取 (Server Data Fetching)
通过逻辑配置组件将asyncData暴露出来
4.客户端数据预取 (Client Data Fetching)
1.在路由导航之前解析数据:
2.匹配要渲染的视图后,再获取数据:
5.Store 代码拆分 (Store Code Splitting)
使用module
7.客户端激活
接下去,我们回到那个点,重新探讨那些问题。
8.Bundle Renderer 指引
1.使用基本 SSR 的问题
这是理所应当的,然而在每次编辑过应用程序源代码之后,都必须停止并重启服务
2.传入 BundleRenderer
如何做
vue-server-renderer
提供一个名为 createBundleRenderer
的 API,用于处理此问题,通过使用 webpack 的自定义插件,server bundle 将生成为可传递到 bundle renderer 的特殊 JSON 文件。
我们已经看到了
优点:
- 内置的 source map 支持(在 webpack 配置中使用
devtool: 'source-map'
) - 在开发环境甚至部署过程中热重载(通过读取更新后的 bundle,然后重新创建 renderer 实例)
- 关键 CSS(critical CSS) 注入(在使用
*.vue
文件时):自动内联在渲染过程中用到的组件所需的CSS。更多细节请查看 CSS 章节。 - 使用 clientManifest 进行资源注入:自动推断出最佳的预加载(preload)和预取(prefetch)指令,以及初始渲染所需的代码分割 chunk。
接下去解释另一个问题。
对于context,如果看到这一步,就算我们没有继续深入更底层的代码,也已经知道,他的具体加工方式。createBundleRenderer.
9.构建配置
1.服务器配置 (Server Config)
这就引发了对source-map功能的联想和对createBundleRenderer的深入思考。显然两边都是有做准备工作的,就是你node读取json格式都要有些问题呢。
2.扩展说明 (Externals Caveats)
1.白名单:我们将 CSS 文件列入白名单,这是因为从依赖模块导入的 CSS 还应该由 webpack 处理。如果你导入依赖于 webpack 的任何其他类型的文件(例如 *.vue
, *.sass
),那么你也应该将它们添加到白名单中。(依赖关系,但是css的打包问题。)
2.如果你使用 runInNewContext: 'once'
或 runInNewContext: true
,那么你还应该将修改 global
的 polyfill 列入白名单
babel-polyfill
会使用使用新的上下文模式。但是*server bundle 中的代码具有自己的 global
对象。
由于在使用 Node 7.6+ 时,在服务器并不真正需要它,所以实际上只需在客户端 entry 导入它。它可能说的是polyfill。
3.客户端配置 (Client Config)
首先我们要了解代码分隔的意义
代码分离是把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。
我们先将webpack的 devtool和代码分隔放在一边。还是探讨,render,context,app(Vue组件)这三个的转换形式的问题。
我们首先引入一个普通的Vue 是怎么一会事情。
当然,是先挂载,再渲染。
首先这里说了客户端配置,你要懂,正如数据预存与预取,引用的就是服务端是客户端的快照。我们目前讲的客户端是服务器将已经激活的客户端拿过来。作为渲染的数据。
下面是正常的客户端
我们看到很简单,就像我之前讲的那个普通的Vue渲染的例子。在webapck打包成index.html之后,他就是返回index.html就好了。因为相关依赖的文件,已经在服务端上传了。
这也就解释了,为什么,当我们手动打开dist文件中的index.HTML 是空白页。
比如,我将Vue打包npm run build ,你用Vue cli搭建的。
路径加载错误。我们上传服务器就是用的相对路径。我们上传到相应路径,在index.html
写相应的路径即可。
这个链接帮你们解决,需要用到webpack。
https://blog.csdn.net/jiaoqi6132/article/details/106563018
最后我们只要搞清楚这种将三种原材料加工与传统的区别到底在哪就好了。但在这之前,我们还需要引入
关于server bundle 和bundle renderer 的范围 ,交集状况,以便我们更加深入理解。
引用这两句话
现在我们已经确定了他们的范围。bundleRender 是将他们集合。
一个clientManifest.一个template,一个serverbundle。
他们和Vue,html,context是什么关系。又有以下几问。
1.我们要探索serverbundle 也就是vue-ssr-server-bundle.json的包括的具体的内容。
2.clientManifest的内容和作用。
3.以及他们之间的关系。
https://www.cnblogs.com/jialikesensi/p/13047778.html
关于source-map的详解
第一个问题
这里看出,他没有包括entry-client.js
楚辞之外,它包括了 ,路由,和处理路由的entry-server.js
第二个问题
vue-ssr-client-manifest.json
client.bundle.js有
这里,也就懂了。
服务端渲染
客户端渲染的时候
那个0.bundle去哪了。
我们只在两个json中找到了它的踪迹
显然单纯的游览器是没有返回的。
那么
vue-ssr-client-manifest.json主要包括了 client.bundle.js。包括了
虽然我们在vue-ssr-server-bundle.json看到了app.js但是没有看到entry-client,但entry-client在clientManifest中。
ssrStream就是把三样已经都交给你完毕了。
我猜,他已经用到了$mount.他是将原来的直接拿过来,为什么?
在HTML中,我们有标签,可以写入,但是在服务端和客户端的混合模式下,我们看到服务器没有吸收index模板。
所以,他应该就是把客户端的数据,拿过来。这就是vue-ssr-client-manifest.json的作用。本质上,我们讨论了几种挂载和渲染方式。也看过,这些东西就是转来转去。
最后的关于三种原材料,他们的设计模式已经完全变了。
在原始中我们就是将三种混合,到时候渲染到HTML ,返回即可。
而服务端和客户端的混合模式,他们是自动注入,因此就会直接引用客户端,到底引用多少,只有开发VUE的知道,就像我们划开了海的接线。但是,海底的鱼可不按国界线的,他们受洋流,也就是底层源码的支配。
还有异步的问题,我会另开一个题。因为这一文章是以context引出webapck打包中,各个资源出现的位置。和重复使用度。
我们可以查看
通过此选项提供一个由 vue-server-renderer/client-plugin
生成的客户端构建 manifest 对象(client build manifest object)。此对象包含了 webpack 整个构建过程的信息,从而可以让 bundle renderer 自动推导需要在 HTML 模板中注入的内容。
就像我分析的一样。
同时关于context的一点补充
那么接下去我们看下服务器的手动注入。
4.手动注入
默认情况下,当提供 template
渲染选项时,资源注入是自动执行的。
但是有时候,你可能需要对资源注入的模板进行更细粒度 (finer-grained) 的控制,或者你根本不使用模板。在这种情况下,你可以在创建 renderer 并手动执行资源注入时,传入 inject: false
。
这些人太懒了,也不会写下哪里。
在 renderToString
回调函数中,你传入的 context
对象会暴露以下方法:
1.context.renderStyles()
这将返回内联 <style>
标签包含所有关键 CSS(critical CSS) ,其中关键 CSS 是在要用到的 *.vue
组件的渲染过程中收集的。
如果提供了 clientManifest
,返回的字符串中,也将包含着 <link rel="stylesheet">
标签内由 webpack 输出(webpack-emitted)的 CSS 文件(例如,使用 extract-text-webpack-plugin
提取的 CSS,或使用 file-loader
导入的 CSS)
2.context.renderState(options?: Object)
此方法序列化 context.state
并返回一个内联的 script,其中状态被嵌入在 window.__INITIAL_STATE__
中。
上下文状态键 (context state key) 和 window 状态键 (window state key),都可以通过传递选项对象进行自定义:
3.context.renderScripts()
- 需要
clientManifest
此方法返回引导客户端应用程序所需的 <script>
标签。当在应用程序代码中使用异步代码分割 (async code-splitting) 时,此方法将智能地正确的推断需要引入的那些异步 chunk。
4.context.renderResourceHints()
- 需要
clientManifest
此方法返回当前要渲染的页面,所需的 <link rel="preload/prefetch">
资源提示 (resource hint)。默认情况下会:
- 预加载页面所需的 JavaScript 和 CSS 文件
- 预取异步 JavaScript chunk,之后可能会用于渲染
使用 shouldPreload
选项可以进一步自定义要预加载的文件。
默认情况下,只有 JavaScript 和 CSS 文件会被预加载,因为它们是启动应用时所必需的。
一个函数,用来控制什么文件应该生成 <link rel="preload">
资源预加载提示 (resource hints)。
对于其他类型的资源(如图像或字体),预加载过多可能会浪费带宽,甚至损害性能,因此预加载什么资源具体依赖于场景。你可以使用 shouldPreload
选项精确控制预加载资源:
5.context.getPreloadFiles()
- 需要
clientManifest
此方法不返回字符串 - 相反,它返回一个数组,此数组是由要预加载的资源文件对象所组成。这可以用在以编程方式 (programmatically) 执行 HTTP/2 服务器推送 (HTTP/2 server push)。
如同我之前分析的一样。
5.Renderer 余剩选项
1.basedir
- 只用于
createBundleRenderer
显式地声明 server bundle 的运行目录。运行时将会以此目录为基准来解析 node_modules
中的依赖模块。只有在所生成的 bundle 文件与外部的 NPM 依赖模块放置在不同位置,或者 vue-server-renderer
是通过 NPM link 链接到当前项目中时,才需要配置此选项。
2.关于webpack引入插件
10.CSS管理
*.vue
单个文件组件内的 <style>
,它提供:
- 与 HTML 并列同级,组件作用域 CSS
- 能够使用预处理器(pre-processor)或 PostCSS
- 开发过程中热重载(hot-reload)
vue-style-loader
(vue-loader
内部使用的 loader),具备一些服务器端渲染的特殊功能:
-
客户端和服务器端的通用编程体验。
-
在使用
bundleRenderer
时,自动注入关键 CSS(critical CSS)。如果在服务器端渲染期间使用,可以在 HTML 中收集和内联(使用
template
选项时自动处理)组件的 CSS。在客户端,当第一次使用该组件时,vue-style-loader
会检查这个组件是否已经具有服务器内联(server-inlined)的 CSS - 如果没有,CSS 将通过<style>
标签动态注入。 -
通用 CSS 提取。
此设置支持使用
extract-text-webpack-plugin
将主 chunk(main chunk) 中的 CSS 提取到单独的 CSS 文件中(使用template
自动注入),这样可以将文件分开缓存。建议用于存在很多公用 CSS 时。内部异步组件中的 CSS 将内联为 JavaScript 字符串,并由
vue-style-loader
处理。
1.启用 CSS 提取
2.从依赖模块导入样式
11.Head
在 2.3.2+ 的版本,你可以通过 this.$ssrContext
来直接访问组件中的服务器端渲染上下文(SSR context)。在旧版本中,你必须通过将其传递给 createApp()
并将其暴露于根实例的 $options
上,才能手动注入服务器端渲染上下文(SSR context) - 然后子组件可以通过 this.$root.$options.ssrContext
来访问它。