公司新做的一个需求,需要满足 seo,前端不愿意使用 php 套页面的方法,所以采用 prerender-spa-plugin 插件做预渲染(没用ssr的原因是改动太大,且只有极少页面需要seo)
prerender-spa-plugin介绍
- prerender-spa-plugin version: 3.4.0
- 代码示例 prerender-cdn-i18n-demo
本文章更多是讲实践部分,介绍可看 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`
);