【调研&方案】预渲染技术

Prerender SPA Plugin

一个灵活的静态网站构建框架,让你更好地使用webpack为SPA应用打包

更多请参考官方文档 预渲染技术

什么是预渲染??

服务端渲染在前端界变得越来越主流。在将网页或应用程序发送给客户之前,在服务器就先呈现了我们想展示的内容。这是一个革命性的想法。

然而,对于PHP、ASP、JSP(以及这样的)站点来说,同样的批评对于今天的服务器端呈现是有效的。它很慢,很容易被打破,并且很难正确地实现。问题是,尽管每个人可能会告诉你,你可能不需要SSR。您可以通过使用预先设定来获得几乎所有的优点(没有缺点)。

预渲染的原理是启动一个无头浏览器(如puperteer),加载应用程序的路由,并将结果保存到静态HTML文件中。然后,可使用以前使用的静态文件-文件服务解决方案来服务它。它只适用于HTML5导航和类似的东西。不需要更改代码或添加服务器端渲染工作区。


使用场景
改善少数营销页面,广告页等的SEO,预渲染就能起到很好的效果.

当然,我们也要承认,在某些场景下,预渲染并不是特别适合:

  • 大量的网页和跳转 使用预渲染会很慢,虽然这种场景并没那么多,但不代表不会出现
  • 动态内容 如果网页中有大量的动态内容,Ajax这类,应确保有占位符组件,直到动态内容加载完后才显示出来,否则页面闪动和重绘的开销会得不偿失

关于 About prerender-spa-plugin

3.x 版本是基于puppeteer的稳定版本。

该插件的目标是提供一个简单、可扩展的预渲染解决方案,可以用于任何网站或用webpack构建的单页面应用。

当然,前提是其他任务是已构建好并可运行的

Examples 示例

特定框架例子 可以在源码中找到

Basic Usage

基础用法 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')

module.exports = {
plugins: [
// 省略其他配置
new PrerenderSPAPlugin({
// 必填,填写output打包出来的路径
staticDir: path.join(__dirname, 'dist'),
// 必填,需要预渲染的路由
routes: ['/', '/about', '/some/deep/nested/route']
})
]
}

Advanced Usage

高级用法 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer

module.exports = {
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
// 可选, 预渲染应该输出到的路径
outputDir: path.join(__dirname, 'prerendered'),
// 可选,根文档的路径
indexPath: path.join(__dirname, 'dist', 'index.html'),
routes: ['/', '/about', '/some/deep/nested/route'],

// 可选 在写入渲染内容到文件之前 允许配置 html和输出路径
// 可以修改渲染, 也可等待返回。 以下是renderedRoute 的格式:
// {
// route: String, // 输出文件将在何处结束(相对于outputDir)
// originalRoute: String, // 在重定向之前,传递给渲染器的路由。.
// html: String // 该路由渲染的html
// }
postProcess(renderedRoute) {
// 忽略任何重定向
renderedRoute.path = renderedRoute.originalPath
// 删除空格. (建议别在生产环境中用)
renderedRoute.html = renderedRoute.html.split(/>[\s]+</gmi).join('><')
// 返回renderedRoute, 其实就是对 传入的 对象进行了一次处理,返回的还是该对象
return renderedRoute
},

// 可选,使用 html-minifier 它会压缩html, 更多参考官方
minify: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
decodeEntities: true,
keepClosingSlash: true,
sortAttributes: true
},

// 服务端配置选项
server: {
// 端口是自动检测的
port: 8001
},

// 渲染器 Renderer的实例,可自行编写
renderer: new Renderer({
// 注入到window对象 kv对 window.__PRERENDER_INJECTED 就可以访问到了
injectProperty: '__PRERENDER_INJECTED',
// 可选,可配置想通过 window 对象注入的可访问值
inject: {
foo: 'bar'
},
// 默认为 0 没有限制
// 由于路由是异步加载的,用该属性限制路由的数量
maxConcurrentRoutes: 4,

// ... 更多的看文档吧
})
})
]
}

可用的渲染器

  • @prerenderer/renderer-puppeteer 已经预先准备好了几百页,并希望得到准确的结果 那就是用它吧
  • @prerenderer/renderer-jsdom 你需要提前阅读成千上万页的内容,但是质量并不是那么重要,你愿意为更高级的案例解决问题

其他

  • JS 在 prerender前不运行

如果您的代码依赖于“body”的存在,那么只需在domcontent重载事件的回调中运行它:(否则会发现在JS运行之前,预编译-spa-plugin将输出页面的内容)

document.addEventListener('DOMContentLoaded', function() {})

如果使用 Vue,将 <div id="root"> mounting 到 body标签上:

1
2
3
4
5
const app = new Vue({ /**/ })

document.addEventListener('DOMContentLoaded', function() {
app.$mount('#root')
})

Inline styles 内联样式

如果您依赖于内联CSS,也就是说,您不会从捆绑包中提取CSS,因此,体验重复的CSS样式标签,可以考虑使用 extract-text-webpack-plugin 将CSS提取到单独的文件中。然后将CSS注入到模板中。使用 html-webpack-plugin 的html文件,或者将其称为外部CSS文件。

无论哪种方式,JS中都不会有任何不必要的样式。

警告

  • 出于显而易见的原因,预安装-spa-插件只适用于使用HTML5历史API的spa。索引。不幸的是,html/hash/路由url将无法工作

  • vue2+ 确保你的根组件与它所替换的预先设计的元素具有相同的id。否则,您将得到重复的内容

Author: Fridolph
Link: http://blog.fridolph.wang/2017/08/01/【调研&方案】预渲染技术/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.