Vue SPA 打包優化實踐

精明的土豆發表於2017-12-15

本文是繼筆者上一篇文章<<Vue SPA 首屏載入優化實踐>>基礎上進一步優化的實踐經驗分享!

隨著我們的專案的增長,打包會越來越慢。每次打包都會將第三方的js打包一遍,但是這些第三方的不會經常變化,如何能將第三方的js只打包一次呢?

非同步元件

我們先將路由元件改成非同步元件。官方文件

// src/router/index.js

...
export default new VueRouter({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: resolve => require(['@/components/HelloWorld'], resolve)
    }
  ]
})
複製程式碼

webpack DllPlugin

webpack提供了一個外掛 DllPlugin ,用於將不常變動的js單獨打包。

  1. 首先我們在src目錄下新建 vendor.jsimport 第三方的js:
import 'babel-polyfill'
import Vue from 'vue'
import Vuex from 'vuex'
import VueRouter from 'vue-router'
import {sync} from 'vuex-router-sync'
import axios from 'axios'
import ElementUI from 'element-ui'
複製程式碼

這裡只需要import進來就行了。

  1. build目錄下新建 webpack.dll.conf.js 配置檔案:
var path = require('path')
var webpack = require('webpack')

var context = path.join(__dirname, '..')

module.exports = {
  entry: {
    vendor: ['./src/vendor.js'] // entry 以專案根目錄為起始路徑
  },
  output: {
    // 將打包後的 js 放到 static 目錄下,build的時候會copy到dist目錄
    path: path.join(context, 'static/js'),
    filename: '[name].dll.js',
    library: '[name]'
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(context, '[name].manifest.json'),
      name: '[name]',
      context: context
    }),
    // 壓縮js程式碼
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      },
      output: { // 刪除打包後的註釋
        comments: false
      }
    })
  ]
}
複製程式碼

這裡的 context 非常關鍵,我這裡以專案的根路徑作為context,manifest.json的位置可自由定義,name要和output裡面的library保持一致。resolve 裡將 webpack.base.conf.js 裡面的 vue$ copy過來,vue用的是vue.esm.js。需要注意的是 dllPlugin 只支援 Array 型別的 entry

  1. package.json 裡面加上 script :build:dll
"scripts": {
  "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
  "start": "npm run dev",
  "lint": "eslint --ext .js,.vue src",
  "build": "node build/build.js",
  "build:dll": "webpack --config build/webpack.dll.conf.js --progress"
}
複製程式碼

執行 npm run build:dll,會在 static/js 裡面生成 vendor.dll.js

Vue SPA 打包優化實踐

到這裡我們的第三方js就打包完了,那麼如何使用呢?

webpack DllReferencePlugin

  1. webpack提供了DllReferencePlugin。修改 build/webpack.base.conf.js
const webpack = require('webpack')

module.exports = {
  ...
  plugins: [
    new webpack.DllReferencePlugin({
      // name引數和dllplugin裡面name一致,可以不傳
      name: 'vendor',
      // dllplugin 打包輸出的manifest.json
      manifest: require('../vendor.manifest.json'),
      // 和dllplugin裡面的context一致
      context: path.join(__dirname, '..')
    })
  ]
  ...
}
複製程式碼

注意:這裡的context一定要和dllplugin裡面的context保持一致

  1. 修改 build/webpack.prod.js。刪除上一篇文章中配置的externals配置項:
module.exports = {
  ...
  // externals: {
  //   'vue': 'Vue',
  //   'vuex': 'Vuex',
  //   'vue-router': 'VueRouter',
  //   'element-ui': 'ELEMENT'
  // }
  ...
  plugins: [
    ...
    // 去掉這裡的CommonsChunkPlugin
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'vendor',
    //   minChunks (module) {
    //     // any required modules inside node_modules are extracted to vendor
    //     return (
    //       module.resource &&
    //       /\.js$/.test(module.resource) &&
    //       module.resource.indexOf(
    //         path.join(__dirname, '../node_modules')
    //       ) === 0
    //     )
    //   }
    // }),
    // 去掉這裡的CommonsChunkPlugin
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'manifest',
    //   minChunks: Infinity
    // }),
    ...
  ]
}
複製程式碼

去掉這兩個CommonsChunkPlugin,因為我們已經通過dllplugin打包了,這裡就不需要了。

  1. index.dev.htmlindex.prod.html 合併成一個檔案 index.html,去掉CDN資源,引入 vendor.dll.js
<head>
  ...
  <link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.0.7/theme-chalk/index.css">
</head>

<body>
  <div id="app"></div>
  <!-- built files will be auto injected -->
  <script src="static/js/vendor.dll.js"></script>
</body>
複製程式碼

這裡 element-ui 的樣式檔案依然採用CDN載入,因為我們沒有將樣式打包到vendor.dll.js裡面

  1. 修改 webpack.dev.conf.jswebpack.prod.conf.js 裡面的模板檔案的配置:
...
plugins: [
  ...
  new HtmlWebpackPlugin({
    template: 'index.html'
  })
]
複製程式碼
  1. 修改 src 裡面 vuexvue-router 的使用:
// src/router/index.js

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

Vue.use(VueRouter)

// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
複製程式碼

至此,我們所有的準備工作就做完了。接下來執行npm run dev,啟動本地開發服務:

Vue SPA 打包優化實踐

打包:npm run build,然後部署到nginx服務,啟動nginx服務:

Vue SPA 打包優化實踐

總結

之所以將 vendor.dll.js 放在 static/js 目錄下,是為了方便開發和釋出使用相同的路徑。為了保持element-ui的js和樣式版本一致,可以在專案中加入lessscss等,然後@import進來就可以了,這樣打包的時候就會單獨打包到css裡面。

示例專案傳送門:vue-spa-starter-kit,歡迎大家吐槽和 star

相關文章