webpack中插件 prerender-spa-plugin 來進行SEO優化(二十四)

vue、react對於開發單頁應用來講帶來了很好的用戶的體驗,可是一樣有缺點,好比首頁加載慢,白屏或SEO等問題的產生。爲何會出現這種狀況呢?咱們以前開發單頁應用是這樣開發的,好比首頁 index.html頁面或許是這樣的:javascript

<!DOCTYPE html> 
<html>
<head>
  <title>webpack+vue項目架構</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
</head>
<body>
  <div id="app">
  </div>
</body>
</html>

在理解以前,咱們仍是和以前同樣,先把咱們的整個項目架構是一個什麼樣的,來簡單的介紹下,方便有個簡單的理解:css

### 目錄結構以下:
demo1                                       # 工程名
|   |--- dist                               # 打包後生成的目錄文件             
|   |--- node_modules                       # 全部的依賴包
|   |--- app
|   | |---index
|   | | |-- views                           # 存放全部vue頁面文件
|   | | | |-- home.vue
|   | | | |-- index.vue
|   | | | |-- java.vue
|   | | | |-- node.vue
|   | | |-- components                      # 存放vue公用的組件
|   | | |-- js                              # 存放js文件的
|   | | |-- app.js                          # vue入口配置文件
|   | | |-- router.js                       # 路由配置文件
|   |--- views
|   | |-- index.html                        # html文件
|   |--- webpack.config.js                  # webpack配置文件 
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json
|   |--- .babelrc                           # babel轉碼文件

而後咱們會經過webpack打包,將單頁應用中的入口文件打包到一個js文件中去,以下配置:html

module.exports = {
  // 入口文件
  entry: {
    main: './app/index/app.js'
  },
  output: {
    filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].js' : 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      hash: true, //爲了開發中js有緩存效果,因此加入hash,這樣能夠有效避免緩存JS。
      template: './views/index.html' // 模版文件
    }),
    new ClearWebpackPlugin(['dist']),
    new ExtractTextPlugin("style.css"),
  ]
};

基本的配置代碼如上所示,它會把 我項目中 /views/index.html 當作模板頁面,而後會把css文件樣式和js文件會自動打包到index.html文件中,以下打包後的文件代碼以下:前端

dist/index.html 源碼以下:vue

<!DOCTYPE html> 
<html>
<head>
  <title>webpack+vue項目架構</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
<link href="style.css?26aab5a9debce5432af2" rel="stylesheet"></head>
<body>
  <div id="app">
  </div>
<script type="text/javascript" src="bundle.js?26aab5a9debce5432af2"></script></body>
</html>

如上打包的主頁index.html文件,咱們能夠看到,頁面上只用一個 div, 而後有一個樣式文件,和一個js文件。並無其餘的。
那麼如上代碼,咱們很容易想到會出現以下幾種缺點:第一:SEO不友好,也就是說,我經過百度或google搜索引擎搜索不到我網站的主頁到,第二是:很容易出現白屏狀況,爲何呢?由於我頁面中的全部的內容都是經過 bundle.js這個動態加載進行,那麼瀏覽器在加載及解析這段時間內,頁面會一直是空白的。也就是咱們說的白屏。固然對於咱們首頁來說,加載也是很是慢的。所以爲了解決這個問題,webpack中的有個插件 prerender-spa-plugin 能夠解決上面這些問題。java

咱們再使用這個插件以前,咱們來理解下咱們以前是怎麼樣進行SEO優化的呢?咱們很早很早以前,咱們是使用 velocity 語法來編寫頁面,而後寫完後把該頁面部署到開發那邊去,那麼這樣就要來回折騰了,而且咱們前端開發成本也很是高。node

那麼第二種方式是使用SSR技術(服務器端渲染),好比Nuxt.js,最主要的思想是,是經過Node.js完成渲染邏輯,而後會將html視圖直接返回給客戶端。這樣的方式也能夠解決SEO的問題。可是對於開發成本仍是比較大。好比須要考慮Node.js環境中的內存泄露,運行速度,併發壓力等問題。而且開發成本增長,研發週期變長等這些問題。所以咱們在webpack中有一種插件能將seo,首屏加載的問題能夠解決掉了。
2. 如何使用 prerender-spa-plugin ?react

1. 安裝命令以下:webpack

npm install prerender-spa-plugin --save-dev

2. 在咱們的webpack.config.js 須要配置以下:git

const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;

var config = {
  // 入口文件
  entry: {
    main: './app/index/app.js'
  },
  output: {
    filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].js' : 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    // ....
  },
  resolve: {
    extensions: ['*', '.js', '.json', '.vue', '.styl']
  },
  devtool: 'cheap-module-eval-source-map',
  devServer: {
    // ....
  },
  plugins: [
    new HtmlWebpackPlugin({
      hash: true, //爲了開發中js有緩存效果,因此加入hash,這樣能夠有效避免緩存JS。
      template: './views/index.html' // 模版文件
    }),
    new ClearWebpackPlugin(['dist']),
    new ExtractTextPlugin("style.css"),
    // .....
  ]
}
// 這裏判斷 若是是 正式環境打包的話,就使用該插件
if (process.env.NODE_ENV === 'production') {
  config.plugins.push(
    new PrerenderSPAPlugin({
      staticDir: path.join(__dirname, '/dist'),
      // 列出須要預渲染的路由
      routes: [ '/home' ],
      renderer: new Renderer({
        inject: {
          foo: 'bar'
        },
        // 監聽到自定事件時捕獲
        renderAfterDocumentEvent: 'render-event'
      })
    })
  )
}
module.exports = config;

下面咱們來理解下PrerenderSPAPlugin中的幾個配置的含義:
staticDir:指的是預渲染輸出的頁面地址。
routes: 指的是須要預渲染的路由地址。
renderer:指的是所採用的渲染引擎是什麼。目前用的是 V3.4.0 版本支持 PuppeteerRenderer。
inject:指的是預渲染過程當中能拿到的值。是否須要渲染這部分代碼,能夠經過該值進行判斷。好比以下代碼:

isshowRender() {
  if(window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.foo =='bar') return;
  this.message = '我是測試預加載攔截';
}

上面執行的方法中的代碼是不會被渲染的。

renderAfterDocumentEvent的含義是監聽 document.dispatchEvent 事件,決定何時開始預渲染。

3. 所以在咱們的app.js 代碼要改爲以下:

import Vue from 'vue';
import Index from './views/index';

// 引入路由
import router from './router';

new Vue({
  el: '#app',
  router: router,
  render: h => h(Index),
  mounted() {
    document.dispatchEvent(new Event('render-event'))
  }
});

在實例化 new Vue的時候 在mounted生命週期中加上代碼:document.dispatchEvent(new Event('render-event')),觸發render-event事件進行渲染。

4. 路由配置(router.js):

import Vue from 'vue';
import VueRouter from 'vue-router';

// 引入組件 
import home from './views/home';
import path from 'path';

// 告訴 vue 使用 vueRouter
Vue.use(VueRouter);

const routes = [
  {
    path: '/home',
    name: 'home',
    component: resolve => require(['./views/home'], resolve),
    // 子路由
    children: [
      {
        path: 'java',
        name: 'java',
        component: resolve => require(['./views/java'], resolve)
      },
      {
        path: 'node',
        name: 'node',
        component: resolve => require(['./views/node'], resolve)
      }
    ]
  },
  {
    path: '*', // 其餘沒有的頁面都重定向到 home頁面去
    redirect: '/home'
  }
]

var router = new VueRouter({
  mode: 'history', // 訪問路徑不帶井號  須要使用 history模式
  // base: path.resolve(__dirname, '/app/index'), // 配置單頁應用的基路徑
  routes: routes
});

export default router;

如上路由配置聽說要改爲 模式變成 mode: 'history'這個模式,該插件纔會渲染。

當咱們認爲配置一切成功的時候,咱們在項目的根目錄中運行 npm run build 的時候,發現打包報錯了,以下提示:

Chromium revision is not downloaded. Run "npm install" or "yarn install" 提示這樣的信息,咱們能夠根據這個信息去百度搜索下,看看是什麼錯誤,百度結果後,他們的意思是須要咱們安裝 puppeteer 這個插件,可是安裝這個插件也是有條件的。咱們須要使用國內Chromium源.以下命令:

npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm i puppeteer

以下圖所示:

一切安裝完成後,咱們再運行 npm run build 後能夠看到在咱們的dist目錄下 會多生成一個 home文件夾了,該文件夾有一個 index.html文件,以下所示:

生成完成後,咱們查看下 dist/home/index.html頁面變成以下:

<!DOCTYPE html><html><head>
  <title>webpack+vue項目架構</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
<link href="style.css?e8ed6db9e5869aa22ba2" rel="stylesheet"><script charset="utf-8" src="1.465ef3291c29aadf53df.js"></script></head>
<body>
  <div id="app"><header><li class="router-link-exact-active router-link-active">Home</li> <a href="/home/java" class="">java</a> <a href="/home/node" class="">node</a></header> <div class="home-container"><h1>歡迎來到Home</h1> <p>我是Home組件</p> <!----></div></div>
<script type="text/javascript" src="main.5aea82d6c5e9feeac132.js?e8ed6db9e5869aa22ba2"></script>
</body></html>

如上代碼,能夠看到,咱們的路由 /home 確實全部的靜態頁面都渲染了。所以實現SEO,頁面不會白屏的問題已經解決了。
github源碼查看

相關文章
相關標籤/搜索