prerender-spa-plugin预渲染原理
- prerender-spa-plugin 利用了 Puppeteer 的爬取页面的功能。 Puppeteer 是一个 Chrome官方出品的 headlessChromenode 库。它提供了一系列的 API, 可以在无 UI 的情况下调用 Chrome 的功能, 适用于爬虫、自动化处理等各种场景。它很强大,所以很简单就能将运行时的 HTML 打包到文件中。原理是在 Webpack 构建阶段的最后,在本地启动一个 Puppeteer 的服务,访问配置了预渲染的路由,然后将 Puppeteer 中渲染的页面输出到 HTML 文件中,并建立路由对应的目录。
简介与使用场景
- 我们知道SPA有很多优点,不过一个缺点就是对(不是Google的)愚蠢的搜索引擎的SEO不友好,为了照顾这些引擎,目前主要有两个方案:服务端渲染(Server Side Rendering)、预渲染(Prerending)。
- 如果你只需要改善少数页面(例如
/
, /about
, /contact
等)的 SEO
,那么你可能需要预渲染。无需使用 web 服务器实时动态编译 HTML (服务端渲染, SSR),而是使用预渲染方式,在构建时(build time)简单地生成针对特定路由的静态 HTML 文件。它主要使用 prerender-spa-plugin 插件,其与SSR一样都可以加快页面的加载速度,并且侵入性更小,在已上线的项目稍加改动也可以轻松引入预渲染机制,而SSR方案则需要将整个项目结构推翻;
- 访问预渲染出来的页面在访问时与
SSR
一样快,并且它将服务端编译HTML的时机提前到了构建时,因此也降低了服务端的压力,如果你的服务器跟我的一样买的 1M1G1核 的小水管服务器 ( 穷 ),那么预渲染可能更适合你。不过SSR和预渲染的使用场景还是有较明显的区别的。预渲染的使用场景更多是简单的静态页面。服务端渲染适用于复杂、较大型、与服务端交互频繁的功能型网站,比如电商网站。
- 文件列表(项目src同级目录创建
favicon.ico
和 index.html
和 webpack.config.js
)
- ico 和 html 直接复制public中的,html 中的 BASE_URL 删掉,否则打包会失败,会提示:ReferenceError: BASE_URL is not defined
- 安装依赖包
npm i -D prerender-spa-plugin webpack webpack-cli cross-env
image.png
./webpack.config.js
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
mode: process.env.NODE_ENV,
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: false,
},
devtool: '#eval-source-map',
plugins: [
new VueLoaderPlugin(),
]
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new HtmlWebpackPlugin({
title: 'PRODUCTION prerender-spa-plugin',
template: 'index.html',
filename: path.resolve(__dirname, 'dist/index.html'),
favicon: 'favicon.ico'
}),
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
routes: [ '/', '/about', '/contact' ], // 路由这块根据自己的路由改写
renderer: new Renderer({
inject: {
foo: 'bar'
},
headless: true,
renderAfterDocumentEvent: 'render-event'
})
})
])
} else {
// NODE_ENV === 'development'
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"'
}
}),
new HtmlWebpackPlugin({
title: 'DEVELOPMENT prerender-spa-plugin',
template: 'index.html',
filename: 'index.html',
favicon: 'favicon.ico'
}),
])
}
-
./src/main.js
,挂载方法重写
new Vue({
el: '#app',
router,
render: h => h(App),
mounted () {
document.dispatchEvent(new Event('render-event'))
}
})
-
./package.json
中添加预渲染打包命令
"prerender-build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
网友评论