masterkong / blog

文章区
21 stars 2 forks source link

Vue预渲染插件prerender-spa-plugin使用全记录 #14

Open masterkong opened 4 years ago

masterkong commented 4 years ago

Vue预渲染插件prerender-spa-plugin使用全记录

服务器端渲染 vs 预渲染 (SSR vs Prerendering)

如果你调研服务器端渲染 (SSR) 只是用来改善少数营销页面(例如 /, /about, /contact 等)的 SEO,那么你可能需要预渲染。无需使用 web 服务器实时动态编译 HTML,而是使用预渲染方式,在构建时 (build time) 简单地生成针对特定路由的静态 HTML 文件。优点是设置预渲染更简单,并可以将你的前端作为一个完全静态的站点。

预渲染页面方式由于不需要web服务器的参与,设置比SSR更简单,特别适合用来展示一些静态页面,比如根据页面UI来自动生成骨架屏。

安装

npm install prerender-spa-plugin --save-dev

prerender-spa-plugin本身的安装非常简单,但是它所依赖的 puppeteer 却有可能让你吃苦头。由于网络原因,直接从npm安装puppeteer有可能会失败,解决办法网络上有很多,最简单就是设置淘宝的镜像源npm config set registry https://registry.npm.taobao.org

puppeteer的介绍可以参考之前的文章 Puppeteer入门简介

验证puppeteer

顺利安装完prerender-spa-plugin后,提前在环境中验证 puppeteer能让你少走点弯路。这点可能在自己开发用的电脑上不明显,但是在部署环境中就有可能踩坑。

本文用了一个简单的node脚本来验证puppeteer,如果成功生成了百度的截屏baidu.png,预渲染插件的使用就成功一大半了。

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox']}); 
  const page = await browser.newPage(); 
  await page.goto('https://www.baidu.com'); 
  await page.screenshot({path: 'baidu.png'}); 
  await browser.close(); 
})();

在 CentOS 7.2 环境中验证就遇到了缺少了依赖库的错误。

/chrome-linux/chrome: error while loading shared libraries: libXss.so.1: cannot open shared object file: No such file or directory
TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md

按照 TROUBLESHOOTING 的提示 安装相应的依赖库

> 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

> yum update nss -y

如果在上述依赖库的安装过程还遇到类似以下报错

Error: Protected multilib versions: libXcursor-1.1.15-1.tl2.x86_64 != libXcursor-1.1.14-2.1.el7.i686
Error: Protected multilib versions: libXi-1.7.9-1.tl2.x86_64 != libXi-1.7.4-2.el7.i686
Error: Protected multilib versions: libXtst-1.2.3-1.tl2.x86_64 != libXtst-1.2.2-2.1.el7.i686

那么先可以删除报错的依赖库再安装

> yum remove 
    libXcursor-1.1.14-2.1.el7.i686 
    libXi-1.7.4-2.el7.i686 
    libXtst-1.2.2-2.1.el7.i686   -y

预渲染配置

prerender-spa-plugin的用法在官方文档有着很详细的说明,这里挑几个主要的配置项说下。(如果webpack中有用到 html-webpack-plugin 插件,一般是在此之后再配置 prerender-spa-plugin)

const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer

module.exports = {
  plugins: [
    ...
    new HtmlWebpackPlugin(),
    ...
    new PrerenderSPAPlugin({
      // 必填 - webpack输出用于预渲染的html文件的路径.
      staticDir: path.join(__dirname, 'dist'),
      // 必填 - 需要预渲染的vue-router路由.
      routes: ['/', '/about'],
      // 可选 - 预渲染的html文件路径。默认为 path.join(staticDir, 'index.html')
      indexPath: path.join(__dirname, 'dist/index.html'),
      // 可选 - 对html文件内容以及生成的最终路径进行自定义处理
      postProcess(renderedRoute) {
        // 删除html中的空白字符
        renderedRoute.html = renderedRoute.html.split(/>[\s]+</gmi).join('><');
        // 将生成的html重命名为prerender.html
        renderedRoute.outputPath = path.join(__dirname, 'dist', renderedRoute.route, 'prerender.html');
        return renderedRoute
      },
      // The actual renderer to use.
      renderer: new Renderer({
        // puppeteer配置参数
        args: ['--no-sandbox', '--disable-setuid-sandbox'], 
        // 当设置为false时,可以看到渲染时调用的浏览器,在调试页面时非常有用
        headless: true,
        // 可选 - 当 document 触发以下事件时才开始渲染页面,使用vue组件时建议配置
        renderAfterDocumentEvent: 'render-event'
      })
    })
  ]
}

配置了 renderAfterDocumentEvent: 'render-event'时,vue组件需要进行相应的修改

new Vue({
    el: '#app',
    ...
    mounted() {
        // 通知 prerender-spa-plugin 可以渲染了
        document.dispatchEvent(new Event('render-event'));
    }
});

踩坑

经过上面的步骤,prerender-spa-plugin插件的配置算是完成了,但并不代表就一切顺利,下面就记录几个遇到的问题。

1、渲染的页面成功生成,但是页面运行后却是静态页面,相应的 js 不起作用

这个问题是 Vue主组件模板也需要设置跟el配置项一样的 id

// App.vue

<template>
    <div id="app">

    </div>
</template>

2、编译过程中一直卡死

这个问题一般是渲染的页面出错或者请求的 css/js 文件没有正常加载,导致没有触发renderAfterDocumentEvent配置的事件名。

参考

qappleh commented 4 years ago

你好 文章可以授权"深圳湾码农"公众号转载吗?会注明作者和原文链接

masterkong commented 4 years ago

你好 文章可以授权"深圳湾码农"公众号转载吗?会注明作者和原文链接

可以