prerender-spa-plugin实战

4,130 阅读2分钟

公司新做的一个需求,需要满足 seo,前端不愿意使用 php 套页面的方法,所以采用 prerender-spa-plugin 插件做预渲染(没用ssr的原因是改动太大,且只有极少页面需要seo)

prerender-spa-plugin介绍

本文章更多是讲实践部分,介绍可看 github.com/chrisvfritz…

简单使用

代码示例:github.com/chrisvfritz…

  • staticDir指定为 webpack output 地址即可
  • 因为该插件是渲染单一页面,所以需要自己指定渲染路由,无路由可指定routes['/']

有个可能遇到的坑为,经过插件渲染后,html 内的<div id="app"></div>会被替换成 vue 组件内容,如果vue 根组件没有id="app则不会被vue再次插入,所以 vue 根组件 id 需要和 html 内指定的 id 一致

多语言使用(如vue-i18n)

正常来说使用多语言的不应该使用prerender打包,因为需要打包多个文件,且需要nginx配置转发,但是任务需要也就加上了

// webpack.conf.js
const getPrerenderSPAPlugin = require('./plugins/getPrerenderSPAPlugin')

  webpackConfig.plugins.push(
    ...getPrerenderSPAPlugin(webpackConfig)
  );
// getPrerenderSPAPlugin.js
const langs = ['cn', 'en'];

/**
 * 获取prerenderSPAPlugin
 * @param webpackConfig
 * @returns {Array}
 */
module.exports = webpackConfig => {
  const result = [];
  const ASSETS_ROOT = webpackConfig.output.path;

  langs.forEach(lang => {
    pushResult(lang);
  });

  function pushResult(lang) {
    const renderConfig = {
      injectProperty: '__PRERENDER_INJECTED__',
      inject: {
        isPrerender: true,
        // 多语言输出
        lang: lang
      },
      headless: true,
      renderAfterDocumentEvent: 'render-event'
    };

    result.push(
      new prerenderSPAPlugin({
        staticDir: ASSETS_ROOT,
        indexPath: path.join(ASSETS_ROOT, 'index.html'),
        routes: routes,
        postProcess(renderedRoute) {
          // 若是首页就打包成相应的html文件
          if (renderedRoute.route === '/') {
            const fileName = lang ? `${lang}.html` : `index.html`;
            renderedRoute.outputPath = path.join(ASSETS_ROOT, fileName);
          } else {
            const fileName = lang
              ? `${renderedRoute.route}.${lang}.html`
              : `${renderedRoute.route}.html`;
            renderedRoute.outputPath = path.join(ASSETS_ROOT, renderedRoute.route, fileName);
          }

          return renderedRoute;
        },
        renderer: new Renderer(renderConfig)
      })
    );
  }

  return result;
};

cdn使用

参考文章 让prerender-spa-plugin支持cdn域名的几种尝试

使用动态的__webpack_public_path__属性,并且替换prerender打包的html内链接

// create a public-path.js
const isPrerender = window.__PRERENDER_INJECTED__ && window.__PRERENDER_INJECTED__.isPrerender
__webpack_public_path__ = isPrerender ? '/' : process.env.CDN_PATH

if (process.env.NODE_ENV === 'development') {
  __webpack_public_path__ = '/'
}

import public-path.js into entry file first

import './public-path'
import Vue from 'vue'
import App from './App'
//...

在 prerender 的postProcess属性做后置处理替换 cdn 域名

// add CDN
renderedRoute.html = renderedRoute.html
.replace(
  /(<script[^<>]*src=\")((?!http|https|\/\/)[^<>\"]*)(\"[^<>]*>[^<>]*<\/script>)/gi,
  `$1${CDN_PATH}$2$3`
)
.replace(
  /(<link[^<>]*href=\")((?!http|https|\/\/)[^<>\"]*)(\"[^<>]*>)/gi,
  `$1${CDN_PATH}$2$3`
)
.replace(
  /(<[source|video|img][^<>]*src=\")((?!http|https|data:image)[^<>\"]*)(\"[^<>]*>)/gi,
  `$1${CDN_PATH}$2$3`
);

完整示例

prerender-cdn-i18n-demo