# 预渲染
## 预渲染简介
SEO和首屏加载速度慢的问题,社区讨论最多的解决方案是同构 SSR,即首屏使用服务端渲染,以后的交互逻辑交给客户端处理,解决了单页应用带来的两个问题,可是也带来了服务器压力增大,学习成本高,对老项目入侵性过强等问题。
## 版本信息
vue: 2.5.2
webpack: 3.6.0
vue-router: 3.0.1
prerender-spa-plugin: 3.4.0
## 使用
这里咱们按照官方 github 的例子敲一下
const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
module.exports = {
plugins: [
...
new PrerenderSPAPlugin({
// 这里选择文件生成目录.
staticDir: path.join(__dirname, 'dist'),
// 这里选择你要预加载的 router 路径,要与在 router 文件里面定义保持一致
routes: [ '/login' ],
})
]
}
还有一些注意事项,假设你的 vue 工程是 vue-cli 2-x 版本的
在 `config/index.js` 里面 `build/assetsPublicPath` 的值为 '/', 这里不能使用相对路径了
const path = require("path") module.exports = { build: { index: path.resolve(__dirname, "../base/index.html"), assetsRoot: path.resolve(__dirname, "../dist"), assetsSubDirectory: "static", assetsPublicPath: "/", } }
还有个配置要注意下在`build/utils.js` 中的 `ExtractTextPlugin.extract` 的 `publicPath` ,不然一些vue中引用的资源会找不到
ExtractTextPlugin.extract({ use: loaders, fallback: 'vue-style-loader', // publicPath: '../../' })
而后在 `router/index.js` 的配置,预渲染要求是 history 模式,若是不声明,则生成的页面都是同一个 html,
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ mode: 'history', routes: [...] })
这个时候能够执行 `npm run build` 打包咱们的项目了,一切正常的话,dist目录应该是这个样子的
├── index.html
├── login
│ └── index.html
└── static
├── css
├── fonts
├── images
├── img
└── js
看到 login 文件夹里面有 index.html,就表明你成功了,
## nginx 配置
这里不须要对这个插件作特殊处理,可是须要对 history 路由作处理,这里贴一下个人ngnix.config配置
server{ listen 8000; server_name localhost; root /dist; error_page 500 502 503 504 /50x.html; location ~^/declaring/ { try_files $uri $uri/ /index.html; } location = /50x.html { root /dist; } }
到这里,重启 nginx, 应该就能够看到效果了
## 遇到的问题:
**生成的 html 里面不是咱们想要的页面 html,或者里面的css,js引用失效。**
检查 `config/index` 里面的 `build/assetsPublicPath` 是否是根目录,
检查 路由是不是 history 模式
检查 `build/utils.js` 中的 `ExtractTextPlugin.extract` 的 `publicPath` 字段是否为空,
**加载页面会有闪屏的效果**
由于预加载是把当前页面,提到构建的时候加载,请求的时候,就会先加载html,而后加载 vue 实例,当 vue 加载好了,vue 会 render 并 push 到 #app 的 DOM 节点上,效果就是闪屏,并且若是页面是动态的,千人千面的,那么用户第一次看到的页面将会是你 build 时获取的数据,而后闪一下,vue 接管页面后正常渲染,
这个问题,RRS也是遇到,vue 也提供了相应的解决方案,咱们移植过来就能够解决了,想了解更详细的,请参考[客户端激活(client-side hydration)](https://ssr.vuejs.org/zh/guide/hydration.html)
postProcess(context) { context.html = context.html.replace('id="app"', 'id="app" data-server-rendered="true"'); return context; },
下面是所有更改:
// build/webpack.prod.conf.js ... const PrerenderSpaPlugin = require('prerender-spa-plugin') const Renderer = PrerenderSpaPlugin.PuppeteerRenderer ... plugins: [ new PrerenderSpaPlugin({ // 编译后html须要存放的路径 staticDir: config.build.assetsRoot, outputDir: config.build.assetsRoot, indexPath: config.build.index, // 列出须要预渲染的路由 routes: ['/', '/login'], postProcess(context) { context.html = context.html.replace('id="app"', 'id="app" data-server-rendered="true"'); return context; }, renderer: new Renderer({ headless: false, renderAfterTime: 5000, // renderAfterDocumentEvent: 'render-event' // document.dispatchEvent(new Event('render-event')) }) }) ] // build/utils if (options.extract) { return ExtractTextPlugin.extract({ use: loaders, fallback: 'vue-style-loader', // publicPath: '../../' }) } // router/indes.js let router = new Router({ mode: 'history', routes: [...] }) // config/index.js build: { ... assetsPublicPath: '/' } // nginx.conf server{ listen 8000; server_name localhost; root /dist; error_page 500 502 503 504 /50x.html; location /api/ { #rewrite ^/api/(.*)$ /$1 break; proxy_pass xxx.xxx.xxx; } location ~^/declaring/ { try_files $uri $uri/ /index.html; } location = /50x.html { root /dist; } }