关于Vue ssr的一点探讨

这很难,里面只是我以比较明显的一个问题,引发对整个ssr的研究。但是我又复习了下Vuex,发现了异步问题。过两天把router也复习了。那异步问题应该就解决了,到时候再出篇稿子。这篇,你可能看不懂,因为不适合新手,对于context的估计,如果有说的不对的,也请各位专家指教。

1.vue ssr 入门

1.一个简单的例子

image-20200604163452471

接下去,我们使用express来继续,得到的是这个

image-20200604163749584

他和render一样。不过服务器渲染需要两个步奏,第一步,先制作出renderer,第二步是使用renderTOString函数

2.使用页面模板

image-20200604164801418

注意 <!--vue-ssr-outlet--> 注释 – 这里将是应用程序 HTML 标记注入的地方。

插值

image-20200604165625551

也可以与 Vue 应用程序实例共享 context 对象,允许模板插值中的组件动态地注册数据。关于context具体的详情会在后面展开。

3.完整的demo

image-20200604182112900

关于context和html

image-20200604182312662

我们发现经过renderToString的函数方法html已经将三个完美融合

分别是读取html 的renderer,和context,和Vue组件。

image-20200604182550841

之后我们获取了经过加工的context。

显然Vue,要么是读取了html,然后将context加工后的数据渲染到了HTML。

image-20200604183846382

加工点,需要两到三个材料。

2.ssr编写过程注意点

1.服务器上的数据响应

禁用响应式数据,还可以避免将「数据」转换为「响应式对象」的性能开销。

2.组件生命周期钩子函数

由于没有动态更新,所有的生命周期钩子函数中,只有 beforeCreatecreated 会在服务器端渲染 (SSR) 过程中被调用。

你应该避免在 beforeCreatecreated 生命周期时产生全局副作用的代码,由于在 SSR 期间并不会调用销毁钩子函数,所以 timer 将永远保留下来。

3.访问特定平台(Platform-Specific) API

4.自定义指令

大多数自定义指令直接操作 DOM,因此会在服务器端渲染 (SSR) 过程中导致错误。有两种方法可以解决这个问题:

  1. 推荐使用组件作为抽象机制,并运行在「虚拟 DOM 层级(Virtual-DOM level)」(例如,使用渲染函数(render function))。
  2. 如果你有一个自定义指令,但是不是很容易替换为组件,则可以在创建服务器 renderer 时,使用 directives 选项所提供"服务器端版本(server-side version)"。

image-20200604172600526

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.全局注册

image-20200604174206214

2.局部注册

1.组件和挂载点

image-20200604174315477

2.组件和组件之间

image-20200604174355879

3.模块挂载

image-20200604174622933

这是在单文件中模块挂载的写法

image-20200604174710491

这是vue-cli自带的。

image-20200604175205129

还有一种比较骚,不作为讲解,就以上。我们不难看出,Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。

那么渲染函数本质就是代替template。组件是什么回事。他的数据是先从组件写上去还是,从父组件中取回数据。都有。

但是有一点确定,他一定会渲染上去。

image-20200604175644486

这里context就是不管是父的还是子的,最终的数据都是渲染上去了。

我们就用这个context去寻找,我们所需要的数据。

到这里为止,我们已经知道这东西来自组件。客户端的context是Vue的一个写死的APi。具体的深入还有很多。请仔细查看。至于服务端的context。我们拉开围绕我们整个ssr的一个大boss。

那么开始下一节

4.源码结构

1.避免状态单例

image-20200604191715990

那么context,是否可以解决了。非常糟糕,有些情况,我们不得的以它为假设,然后进行输出。

img

image-20200605053611260

image-20200605053945728

接下去就是关于server bundle 和bundle renderer 进行讨论。

5.路由和代码分隔

1.使用使用 vue-router 的路由

image-20200605054244416

image-20200605054523881

image-20200605054956147

到目前我们存在了两个问题,结合别人的demo。

image-20200605055052944

image-20200605055028605

1.为什么调用renderTostream,居然会跑到entry-server,我们看到

image-20200605055143798

image-20200605055239604

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官方给出的路由。再结合上面提到的第一个问题。

image-20200604191715990

image-20200605061143117

image-20200605061155937

这两个应该请求是同一个功能的文件。是的,在后者中,我们看到他把context送去给Vue 实例加工。返回一个app。

image-20200605061343589

前者也是一样createApp中主要功能就是new Vue ,进行实例加工。将Vue实例和context进行融合。

image-20200605062306198

从本质上,我们彻底搞清楚了,服务器渲染需要的两大部分。同时我们意识到渲染的时候,已经配置renderer的时候,没有那么简单。

又有新的问题

image-20200605062934955

经过打包的入口变成了

image-20200605063000600

到底发生了什么。

我们进入其中

image-20200605063241098

我们也找到了

image-20200605063342786

image-20200605063541068

我们有理由相信他将这两个混为了一个app.js,所以我们还要对webpack进行研究。、

而且你已经发现了他们两者的不同

image-20200605064657640

image-20200605064843612

在接下去的一段章节,可能不会讲到这,你需要保持专注。

2.代码分隔

1.异步读取

image-20200605063721555

2.onready的作用

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

3.异步组件路由配置

image-20200605063805601

image-20200605063835791

6.数据预取和状态

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

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

2.首先,在服务器端,我们可以在渲染之前预取数据,并将数据填充到 store 中。此外,我们将在 HTML 中序列化(serialize)和内联预置(inline)状态。这样,在挂载(mount)到客户端应用程序之前,可以直接从 store 获取到内联预置(inline)状态。

2.带有逻辑配置的组件 (Logic Collocation with Components)

image-20200605065328529

3.服务器端数据预取 (Server Data Fetching)

通过逻辑配置组件将asyncData暴露出来

image-20200605065946098

image-20200605065915063

4.客户端数据预取 (Client Data Fetching)
1.在路由导航之前解析数据:

image-20200605070424423

2.匹配要渲染的视图后,再获取数据:

image-20200605070818694

image-20200605070648283

image-20200605071010006

image-20200605175625307

5.Store 代码拆分 (Store Code Splitting)

使用module

image-20200605071125339

image-20200605071730780

7.客户端激活

image-20200605072544444

image-20200605072625477

接下去,我们回到那个点,重新探讨那些问题。

8.Bundle Renderer 指引

1.使用基本 SSR 的问题

这是理所应当的,然而在每次编辑过应用程序源代码之后,都必须停止并重启服务

image-20200605073924292

2.传入 BundleRenderer

如何做

vue-server-renderer 提供一个名为 createBundleRenderer 的 API,用于处理此问题,通过使用 webpack 的自定义插件,server bundle 将生成为可传递到 bundle renderer 的特殊 JSON 文件。

我们已经看到了

image-20200605064657640

优点:

  • 内置的 source map 支持(在 webpack 配置中使用 devtool: 'source-map'
  • 在开发环境甚至部署过程中热重载(通过读取更新后的 bundle,然后重新创建 renderer 实例)
  • 关键 CSS(critical CSS) 注入(在使用 *.vue 文件时):自动内联在渲染过程中用到的组件所需的CSS。更多细节请查看 CSS 章节。
  • 使用 clientManifest 进行资源注入:自动推断出最佳的预加载(preload)和预取(prefetch)指令,以及初始渲染所需的代码分割 chunk。

image-20200605074249126

image-20200605074504750

接下去解释另一个问题。

对于context,如果看到这一步,就算我们没有继续深入更底层的代码,也已经知道,他的具体加工方式。createBundleRenderer.

9.构建配置

1.服务器配置 (Server Config)

image-20200605080949441

这就引发了对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,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。

image-20200605082821051

我们先将webpack的 devtool和代码分隔放在一边。还是探讨,render,context,app(Vue组件)这三个的转换形式的问题。

我们首先引入一个普通的Vue 是怎么一会事情。

image-20200605083449305

当然,是先挂载,再渲染。

首先这里说了客户端配置,你要懂,正如数据预存与预取,引用的就是服务端是客户端的快照。我们目前讲的客户端是服务器将已经激活的客户端拿过来。作为渲染的数据。

下面是正常的客户端

image-20200605084159184

我们看到很简单,就像我之前讲的那个普通的Vue渲染的例子。在webapck打包成index.html之后,他就是返回index.html就好了。因为相关依赖的文件,已经在服务端上传了。

image-20200605084408569

这也就解释了,为什么,当我们手动打开dist文件中的index.HTML 是空白页。

比如,我将Vue打包npm run build ,你用Vue cli搭建的。

image-20200605085111339

image-20200605085209947

image-20200605085226479

路径加载错误。我们上传服务器就是用的相对路径。我们上传到相应路径,在index.html

写相应的路径即可。

这个链接帮你们解决,需要用到webpack。

https://blog.csdn.net/jiaoqi6132/article/details/106563018

最后我们只要搞清楚这种将三种原材料加工与传统的区别到底在哪就好了。但在这之前,我们还需要引入

image-20200605085921987

关于server bundle 和bundle renderer 的范围 ,交集状况,以便我们更加深入理解。

引用这两句话

image-20200605090137469

image-20200605090150687

image-20200605090320965

现在我们已经确定了他们的范围。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的详解

第一个问题

image-20200605091541460

image-20200605091742477

这里看出,他没有包括entry-client.js

楚辞之外,它包括了 ,路由,和处理路由的entry-server.js

image-20200605091934976

第二个问题

vue-ssr-client-manifest.json

image-20200605093500238

client.bundle.js有

image-20200605093523925

image-20200605093559958

这里,也就懂了。

服务端渲染

image-20200605093726536

客户端渲染的时候

image-20200605093822976

那个0.bundle去哪了。

image-20200605094120738

我们只在两个json中找到了它的踪迹

显然单纯的游览器是没有返回的。

那么

vue-ssr-client-manifest.json主要包括了 client.bundle.js。包括了

虽然我们在vue-ssr-server-bundle.json看到了app.js但是没有看到entry-client,但entry-client在clientManifest中。

image-20200605094559720

image-20200605094756812

ssrStream就是把三样已经都交给你完毕了。

我猜,他已经用到了$mount.他是将原来的直接拿过来,为什么?

image-20200604182112900

在HTML中,我们有标签,可以写入,但是在服务端和客户端的混合模式下,我们看到服务器没有吸收index模板。

所以,他应该就是把客户端的数据,拿过来。这就是vue-ssr-client-manifest.json的作用。本质上,我们讨论了几种挂载和渲染方式。也看过,这些东西就是转来转去。

最后的关于三种原材料,他们的设计模式已经完全变了。

在原始中我们就是将三种混合,到时候渲染到HTML ,返回即可。

而服务端和客户端的混合模式,他们是自动注入,因此就会直接引用客户端,到底引用多少,只有开发VUE的知道,就像我们划开了海的接线。但是,海底的鱼可不按国界线的,他们受洋流,也就是底层源码的支配。

还有异步的问题,我会另开一个题。因为这一文章是以context引出webapck打包中,各个资源出现的位置。和重复使用度。

我们可以查看

image-20200605101433641

通过此选项提供一个由 vue-server-renderer/client-plugin 生成的客户端构建 manifest 对象(client build manifest object)。此对象包含了 webpack 整个构建过程的信息,从而可以让 bundle renderer 自动推导需要在 HTML 模板中注入的内容。

就像我分析的一样。

同时关于context的一点补充

image-20200605101647529

那么接下去我们看下服务器的手动注入。

4.手动注入

默认情况下,当提供 template 渲染选项时,资源注入是自动执行的。

image-20200605100623549

但是有时候,你可能需要对资源注入的模板进行更细粒度 (finer-grained) 的控制,或者你根本不使用模板。在这种情况下,你可以在创建 renderer 并手动执行资源注入时,传入 inject: false

image-20200605100809201

这些人太懒了,也不会写下哪里。

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),都可以通过传递选项对象进行自定义:

image-20200605101026533

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 选项精确控制预加载资源:

image-20200605101204059

image-20200605101227762

5.context.getPreloadFiles()

  • 需要 clientManifest

此方法不返回字符串 - 相反,它返回一个数组,此数组是由要预加载的资源文件对象所组成。这可以用在以编程方式 (programmatically) 执行 HTTP/2 服务器推送 (HTTP/2 server push)。

如同我之前分析的一样。

image-20200605101322438

5.Renderer 余剩选项

1.basedir

  • 只用于 createBundleRenderer

显式地声明 server bundle 的运行目录。运行时将会以此目录为基准来解析 node_modules 中的依赖模块。只有在所生成的 bundle 文件与外部的 NPM 依赖模块放置在不同位置,或者 vue-server-renderer 是通过 NPM link 链接到当前项目中时,才需要配置此选项。

2.关于webpack引入插件

image-20200605103229475

10.CSS管理

*.vue 单个文件组件内的 <style>,它提供:

  • 与 HTML 并列同级,组件作用域 CSS
  • 能够使用预处理器(pre-processor)或 PostCSS
  • 开发过程中热重载(hot-reload)

vue-style-loadervue-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 提取

image-20200605104312055

2.从依赖模块导入样式

image-20200605104449827

11.Head

在 2.3.2+ 的版本,你可以通过 this.$ssrContext 来直接访问组件中的服务器端渲染上下文(SSR context)。在旧版本中,你必须通过将其传递给 createApp() 并将其暴露于根实例的 $options 上,才能手动注入服务器端渲染上下文(SSR context) - 然后子组件可以通过 this.$root.$options.ssrContext 来访问它。

image-20200605104855182

image-20200605105051993

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值