vue預渲染實踐總結

# 預渲染

## 預渲染簡介

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;
        }
}
相關文章
相關標籤/搜索