预渲染实战指南

3,347 阅读3分钟

基本配置参考官方文档:github.com/chrisvfritz…

基本原理介绍

这是一个将单页应用预渲染的webpack插件,借助html-webpack-plugin将打包好的js块注入到指定的html文件中,然后prerender-spa-plugin会调用无头浏览器读取该html文件,读取里面引入的静态资源,渲染出完整的html文件到指定目录下。客户端访问该站点时,通过nginx的路由配置,将请求映射到指定的预渲染后的html文件下,返回给客户端,无需在客户端动态渲染页面,有利于SEO优化和加快首屏加载速度。

单页应用(一个入口)的配置

官方的demo就是按照这个情况配置的,只支持一个入口的情况,步骤如下:

  1. 使用html-webpack-plugin将打包后的js文件,css文件注入到index.html中,生成指定的html文件,供预渲染时,让无头浏览器读取。
new HtmlWebpackPlugin({
    title: 'PRODUCTION prerender-spa-plugin',
    template: 'public/index.html',
    filename: path.resolve(__dirname, 'output/index.html'),
})
  1. 使用prerender-spa-plugin读取上面生成html文件,预渲染出html文件
new PrerenderSPAPlugin({
    staticDir: path.join(__dirname, 'output'),
    routes: ['/home'],

    renderer: new Renderer({
        inject: {
            foo: 'bar'
        },
        // headless: true,
        renderAfterDocumentEvent: 'render-event'
    }),
})

多页应用(多个入口)的配置

若项目是多页配置,则需要修改配置文件,上面的单页配置默认会把所有的静态资源js,css都打包到index.html,造成冲突。多页配置的核心点是要把js,css分开打包,所以我们要根据入口创建多个实例,将页面对应的js,css打包到对应的html文件中。关键步骤如下:

  1. 实例化多个HtmlWebpackPlugin对象,根据多页路由生成对应的html文件
new HtmlWebpackPlugin({
    title: 'PRODUCTION prerender-spa-plugin',
    template: 'public/index.html',
    filename: path.resolve(__dirname, 'output/home.html'),
    //根据打包后的js,生成首页的home.html
    chunks: ['chunk-vendors', 'chunk-common', 'home'] 
})
new HtmlWebpackPlugin({
    title: 'PRODUCTION prerender-spa-plugin',
    template: 'public/index.html',
    filename: path.resolve(__dirname, 'output/product.html'),
    //根据打包后的js,生成产品页的product.html
    chunks: ['chunk-vendors', 'chunk-common', 'product'] 
})
...
  1. 实例化多个HtmlWebpackPlugin对象,分别将上面生成的html文件进行预渲染
new PrerenderSPAPlugin({
    // Required - The path to the webpack-outputted app to prerender.
    staticDir: path.join(__dirname, 'output'),
    //指定读取上面生成的home.html文件
    indexPath: path.join(__dirname, 'output', 'home.html'),
    // Required - Routes to render.
    routes: ['/home'],
    
    renderer: new Renderer({
        inject: {
            foo: 'bar'
        },
        headless: true,
        renderAfterDocumentEvent: 'render-event'
    }),
    postProcess(renderedRoute) {
       //Optional - Allows you to customize the HTML and output path before
       // writing the rendered contents to a file.
        return renderedRoute;
    }
    
new PrerenderSPAPlugin({
    // Required - The path to the webpack-outputted app to prerender.
    staticDir: path.join(__dirname, 'output'),
    //指定读取上面生成的product.html文件
    indexPath: path.join(__dirname, 'output', 'product.html'),
    // Required - Routes to render.
    routes: ['/product'],
    
    renderer: new Renderer({
        inject: {
            foo: 'bar'
        },
        headless: true,
        renderAfterDocumentEvent: 'render-event'
    }),
    postProcess(renderedRoute) {
       //Optional - Allows you to customize the HTML and output path before
       // writing the rendered contents to a file.
        return renderedRoute;
    }   
...

注意事项

  1. 注意静态资源的路径问题,预渲染的html文件中引入的静态资源,根路径是在当前编译产出的文件夹,即在output下寻找该路径,若后端有对静态资源的引入做路径映射,则预渲染时无头浏览器可能获取资源失败,即:构建环境下的publicPath只能设置为'/',然后可以更改assetsDir的路径设置
publicPath: ENV_LOCAL ? '/' : '/',
outputDir: 'output',
assetsDir: 'open-static/website',
  1. 修改nginx中location的匹配规则,将页面路由映射到对应预渲染生成的html文件路径,示例中为home/home.html
location / {
    root /home/bae/orp/webroot;
    fastcgi_pass    $php_upstream;
    include         fastcgi.conf;

    rewrite ^/union/product([^\?]*)?((\?.*)?)$ /product/product.html$1$2 break;
    rewrite ^/union/([^\?]*)?((\?.*)?)$ /home/home.html$1$2 break;
    }
  1. PrerenderSPAPlugin配置中的路由必须和页面的路由匹配上,无头浏览器会根据路由渲染不同的内容。

编译问题

若编译构建环境是在centos下,则打开无头浏览器预渲染会报错,需要安装系统依赖文件。

参考:github.com/GoogleChrom…