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
客户端通过 HTTP 向服务器请求资源,当必要的资源都加载完毕后,执行 new Vue() 开始实例化并渲染页面。
服务端渲染过程
访问服务端渲染的网站。
服务器会查看当前路由组件需要哪些资源文件,然后将这些文件的内容填充到 HTML 文件。如果有 asyncData() 函数,就会执行它进行数据预取并填充到 HTML 文件里,最后返回这个 HTML 页面。
当客户端接收到这个 HTML 页面时,可以马上就开始渲染页面。与此同时,页面也会加载资源,当必要的资源都加载完毕后,开始执行 new Vue() 开始实例化并接管页面。
从上述两个过程中,可以看出,区别就在于第二步。客户端渲染的网站会直接返回 HTML 文件,而服务端渲染的网站则会渲染完页面再返回这个 HTML 文件。
这样做的好处是什么?是更快的内容到达时间 (time-to-content)。
假设你的网站需要加载完 abcd 四个文件才能渲染完毕。并且每个文件大小为 1 M。
这样一算:客户端渲染的网站需要加载 4 个文件和 HTML 文件才能完成首页渲染,总计大小为 4M(忽略 HTML 文件大小)。而服务端渲染的网站只需要加载一个渲染完毕的 HTML 文件就能完成首页渲染,总计大小为已经渲染完毕的 HTML 文件(这种文件不会太大,一般为几百K,我的个人博客网站(SSR)加载的 HTML 文件为 400K)。这就是服务端渲染更快的原因。
客户端接管页面
对于服务端返回来的 HTML 文件,客户端必须进行接管,对其进行 new Vue() 实例化,用户才能正常使用页面。
建议先阅读官方指南——Vue.js 服务器端渲染指南,再回到本文开始阅读。
本文将分成以下两部分:
好了,下面开始正文。
简述 Vue SSR 过程
客户端渲染过程
<div id="app"></div>
的 HTML 文件。new Vue()
开始实例化并渲染页面。服务端渲染过程
asyncData()
函数,就会执行它进行数据预取并填充到 HTML 文件里,最后返回这个 HTML 页面。new Vue()
开始实例化并接管页面。从上述两个过程中,可以看出,区别就在于第二步。客户端渲染的网站会直接返回 HTML 文件,而服务端渲染的网站则会渲染完页面再返回这个 HTML 文件。
这样做的好处是什么?是更快的内容到达时间 (time-to-content)。
假设你的网站需要加载完 abcd 四个文件才能渲染完毕。并且每个文件大小为 1 M。
这样一算:客户端渲染的网站需要加载 4 个文件和 HTML 文件才能完成首页渲染,总计大小为 4M(忽略 HTML 文件大小)。而服务端渲染的网站只需要加载一个渲染完毕的 HTML 文件就能完成首页渲染,总计大小为已经渲染完毕的 HTML 文件(这种文件不会太大,一般为几百K,我的个人博客网站(SSR)加载的 HTML 文件为 400K)。这就是服务端渲染更快的原因。
客户端接管页面
对于服务端返回来的 HTML 文件,客户端必须进行接管,对其进行
new Vue()
实例化,用户才能正常使用页面。如果不对其进行激活的话,里面的内容只是一串字符串而已,例如下面的代码,点击是无效的:
那客户端如何接管页面呢?下面引用一篇文章中的内容:
小结
不管是客户端渲染还是服务端渲染,都需要等待客户端执行
new Vue()
之后,用户才能进行交互操作。但服务端渲染的网站能让用户更快的看见页面。从零开始搭建 SSR 项目
配置 weback
webpack 配置文件共有 3 个:
webpack.base.config.js
,基础配置文件,客户端与服务端都需要它。webpack.client.config.js
,客户端配置文件,用于生成客户端所需的资源。webpack.server.config.js
,服务端配置文件,用于生成服务端所需的资源。webpack.base.config.js
基础配置文件基础配置文件比较简单,
output
属性的意思是打包时根据文件内容生成文件名称。module
属性配置不同文件的解析 loader。webpack.client.config.js
客户端配置文件客户端配置文件中的
config.optimization
属性是打包时分割代码用的。它的作用是将第三方库都打包在一起。其他插件作用:
MiniCssExtractPlugin
插件, 将 css 提取出来单独打包。CssMinimizerPlugin
插件,压缩 css。CompressionPlugin
插件,将资源压缩成 gzip 格式(大大提升传输效率)。另外还需要在 node 服务器上引入compression
插件配合使用。WebpackBar
插件,打包时显示进度条。webpack.server.config.js
服务端配置文件服务端打包和客户端不同,它将所有文件一起打包成一个文件
server-bundle.js
。同时解析 css 需要使用vue-style-loader
,这一点在官方指南中有说明:配置服务器
生产环境
pro-server.js
生产环境服务器配置文件从代码中可以看到,当首次加载页面时,需要调用
createBundleRenderer()
生成一个 renderer,它的参数是打包生成的vue-ssr-server-bundle.json
和vue-ssr-client-manifest.json
文件。当返回 HTML 文件后,页面将会被客户端接管。在文件的最后有一行代码
app.get('*', render)
,它表示所有匹配不到的请求都交给它处理。所以如果你写了 ajax 请求处理函数必须放在前面,就像下面这样:否则你的页面会打不开。
开发环境
开发环境的服务器配置和生产环境没什么不同,区别在于开发环境下的服务器有热更新。
一般用 webpack 进行开发时,简单的配置一下 dev server 参数就可以使用热更新了,但是 SSR 项目需要自己配置。
由于 SSR 开发环境服务器的配置文件
setup-dev-server.js
代码太多,我对其进行简化后,大致代码如下:同时需要在 webpack 的入口文件加上这一行代码
webpack-hot-middleware/client?reload=true
。然后使用
node dev-server.js
来开启前端代码热更新。热更新主要使用了两个插件:
webpack-dev-middleware
和webpack-hot-middleware
。顾名思义,看名称就知道它们的作用,webpack-dev-middleware
的作用是生成一个与 webpack 的 compiler 绑定的中间件,然后在 express 启动的 app 中调用这个中间件。webpack-hot-middleware
插件的作用就是热更新,它需要配合HotModuleReplacementPlugin
和webpack-dev-middleware
一起使用。打包文件
vue-ssr-client-manifest.json
和vue-ssr-server-bundle.json
webpack 需要对源码打包两次,一次是为客户端环境打包的,一次是为服务端环境打包的。
为客户端环境打包的文件,和以前我们打包的资源一样,不过多出了一个
vue-ssr-client-manifest.json
文件。服务端环境打包只输出一个vue-ssr-server-bundle.json
文件。vue-ssr-client-manifest.json
包含了客户端环境所需的资源名称:从上图中可以看到有三个关键词:
vue-ssr-server-bundle.json
文件:填坑记录
1.
[vue-router] failed to resolve async component default: referenceerror: window is not defined
由于在一些文件或第三方文件中可能会用到 window 对象,并且 node 中不存在 window 对象,所以会报错。
此时可在
src/app.js
文件加上以下代码进行判断:2.
mini-css-extract-plugin
插件造成ReferenceError: document is not defined
使用
mini-css-extract-plugin
插件打包的的server bundle
, 会使用到 document。由于 node 环境中不存在 document 对象,所以报错。解决方案:样式相关的 loader 不要放在
webpack.base.config.js
文件,将其分拆到webpack.client.config.js
和webpack.client.server.js
文件。其中mini-css-extract-plugin
插件要放在webpack.client.config.js
文件配置。base
client
server
3. 开发环境下跳转页面样式不生效,但生产环境正常。
由于开发环境使用的是
memory-fs
插件,打包文件是放在内存中的。如果此时dist
文件夹有刚才打包留下的资源,就会使用dist
文件夹中的资源,而不是内存中的资源。并且开发环境和打包环境生成的资源名称是不一样的,所以就造成了这个 BUG。解决方法是执行
npm run dev
时,删除dist
文件夹。所以要在npm run dev
对应的脚本中加上rimraf dist
。4.
[vue-router] Failed to resolve async component default: ReferenceError: document is not defined
不要在有可能使用到服务端渲染的页面访问 DOM,如果有这种操作请放在
mounted()
钩子函数里。如果你引入的数据或者接口有访问 DOM 的操作也会报这种错,在这种情况下可以使用
require()
。因为require()
是运行时加载的,所以可以这样使用:修改后:
修改后可以正常使用。
5. 开发环境下,开启服务器后无任何反应,也没见控制台输出报错信息。
这个坑其实是有报错信息的,但是没有输出,导致以为没有错误。
在
setup-dev-server.js
文件中有一行代码if (stats.errors.length) return
,如果有报错就直接返回,不执行后续的操作。导致服务器没任何反应,所以我们可以在这打一个console.log
语句,打印报错信息。小结
这个 DEMO 是基于官方 DEMO vue-hackernews-2.0 改造的。不过官方 DEMO 发表于 4 年前,最近修改时间是 2 年前,很多选项参数已经过时了。并且官方 DEMO 需要翻墙才能使用。所以我在此基础上对其进行了改造,改造后的 DEMO 放在 Github 上,它是一个比较完善的 DEMO,可以在此基础上进行二次开发。
如果你不仅仅满足于一个 DEMO,建议看一看我的个人博客项目,它原来是客户端渲染的项目,后来重构为服务端渲染,绝对实战。
参考资料
更多文章,敬请关注
The text was updated successfully, but these errors were encountered: