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


vue、react對於開發單頁應用來說帶來了很好的用戶的體驗,但是同樣有缺點,比如首頁加載慢,白屏或SEO等問題的產生。為什么會出現這種情況呢?我們之前開發單頁應用是這樣開發的,比如首頁 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">
</head>
<body>
  <div id="app">
  </div>
</body>
</html>

在理解之前,我們還是和之前一樣,先把我們的整個項目架構是一個什么樣的,來簡單的介紹下,方便有個簡單的理解:

### 目錄結構如下:
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文件中去,如下配置:

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 源碼如下:

<!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 可以解決上面這些問題。

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

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

1. 安裝命令如下:

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

2. 在我們的webpack.config.js 需要配置如下:

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源碼查看


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM