解决问题

  • 优化SPA首屏显示
  • 同时也能优化SEO搜索

    原理:

    SPA在首屏显示时,html中并无任何展示内容(loading之类的除外),需在app.jsvendor.xxx.js加载完成后,再根据路由加载相应的模块文件chunk.xxxx.js

  • js文件过大,网络不好的情况下会增加首屏的白屏时间

  • 对于性能不好的手机,在js文件过大时运行也需要一定时间

优化方案

在服务端返回的html文件中已经带有对应的内容展示,就不需要等到js加载完成后才渲染

  • 服务端渲染SSR

    • 适用于所有页面(静态或者动态数据)
    • 针对于现有项目改动较大,需要权衡实际的需求和相应的消耗时间,以及出现的问题
  • 预渲染

    • 适用于没有数据变化的静态页面
    • 改动较小,不影响不做预渲染的页面

prerender-spa-plugin

预渲染主要使用插件prerender-spa-plugin

原理

该插件是在打包构建时,将对应的路由生成为相应的index.html文件:
比如路由:/about,则会在打包路径下生成文件/about/index.html

使用方法

安装

1
npm i prerender-spa-plugin -D

安装过程中遇到的问题

  • 该插件依赖google puppeteer, 下载的时候需要翻墙,而且包很大,需要下载很久;可改为使用cnpm安装
    1
    cnpm i prerender-spa-plugin -D
对于不想使用`cnpm`作为常用的包管理器,可以将打包命令改成类似如下方式:
1
cnpm i prerender-spa-plugin -D && vue-cli-service build --mode=dev
  • centos系统需要7及以上版本才能使用
  • linux中构建打包时提示so文件找不到时,需要安装以下依赖库
    1
    yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 ipa-gothic-fonts xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc -y

使用方法

vue/cli3

路由需要使用history模式
vue.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
module.exports = {
configureWebpack: config => {// webpack配置
const plugins = []
// 预渲染处理
if (process.env.NODE_ENV === 'production') {
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
plugins.push(
new PrerenderSPAPlugin({
// 生成文件的路径,也可以与webpakc打包的一致。
staticDir: path.join(__dirname, process.env.OUTPUT_DIR),
// 对应自己的路由文件,比如a有参数,就需要写成 /a/param1。
routes: ['/about'],
// 这个很重要,如果没有配置这段,也不会进行预编译
renderer: new Renderer({
headless: true,
// 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
renderAfterDocumentEvent: 'render-event'
}),
minify:{
minifyCSS: true,
minifyJS: true
},
postProcess(context) {
// 去掉app的隐藏处理
context.html = context.html.replace(/#app\{display:none\}/g,'').replace(/id="app"/g, 'id="app" data-server-rendered="true" ')
// 删除路由动态添加的script和preload标签。
const arr = context.html.split('</head>')
context.html = arr[0].replace(/<script\b[^>]*><\/script>/g,'').replace(/<link[^>]*preload[^>]*>/g,'')
+ '</head>' + arr[1]
return context;
}
})
)
}
return {
plugins
}
}
}

main.js

mounted钩子函数中增加事件处理:

1
2
3
4
5
6
mounted () {
// 延时500ms。
setTimeout(() => {
document.dispatchEvent(new Event('render-event'))
}, 500)
}

问题

  • 预渲染的页面有图片,在js加载后,图片会出现闪烁(暂无解决方案)
  • main.js中的事件可能导致预渲染页面生成的没有内容

    1
    2
    3
    4
    5
    6
    mounted () { // 预渲染处理 2020.04.03
    // 延时500ms。
    setTimeout(() => {
    document.dispatchEvent(new Event('render-event'))
    }, 500)
    }

    解决方法:
    通过增加一定的延时

  • 生成的预渲染页head中会有对应的模块js,可以去掉,避免在渲染的时候阻塞页面
    去掉方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    postProcess(context) {
    // 去掉app的隐藏处理
    context.html = context.html.replace(/#app\{display:none\}/g,'').replace(/id="app"/g, 'id="app" data-server-rendered="true" ')
    // 删除路由动态添加的script和preload标签。
    const arr = context.html.split('</head>')
    context.html = arr[0].replace(/<script\b[^>]*><\/script>/g,'').replace(/<link[^>]*preload[^>]*>/g,'')
    + '</head>' + arr[1]
    return context;
    }

本文地址: http://gehaiqing.com/2020/04/20/vue-prerender/