記一次使用 vue-admin-template 的優化歷程

本文原文javascript

前言

公司有好幾個項目都有後臺管理系統,爲了方便開發,因此選擇了 vue 中比較火的 後臺模板 做爲基礎模板進行開發。可是,開始用的時候,做者並無對此進行優化,到項目上線的時候,才發現,打包出來的文件都十分之大,就一個 vendor 就有 770k 的體積(下圖是基礎模板,什麼都沒加打包後的文件信息):css

經過 webpack-bundle-analyzer 進行分析可得,體積主要來源於 餓了麼UI(體積爲 500k),由於沒對其進行部分引入拆分組件,致使 webpack 把整個組件庫都打包進去了。其次就是 vue 自己,體積也達到了 80k 之大。html

因此,對其進行打包優化,是一件刻不容緩的事情。前端

優化

優化主要目的有:vue

  1. 加快資源加載速度,減小用戶等待的時間和首頁白屏時間,提升用戶體驗。
  2. 加快打包速度,不要將時間浪費在等待打包上。

解決第一個問題,不少人都會想到資源文件放在 CDN 上就行了,沒錯,此次咱們就是經過 CDN 來解決加載問題。java

CDN - 提升加載速度

像 vue, element ui 這些比較成熟的框架/組件庫,通常都有免費、高速、公共的 cdn 供開發者使用,鑑於大部分用戶均在國內,因此此次使用了 bootcdn 這個庫。該庫熱門資源比較齊全,各個版本都有,並且國內訪問速度很快,簡直是開發者的福音。node

index.html 中引入 vue 和 餓了麼組件。webpack

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>vue-admin-template</title>
    <!-- 同時也要引入對應版本的 css -->
    <link href="https://cdn.bootcss.com/element-ui/2.3.2/theme-chalk/index.css" rel="stylesheet">
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <!-- 先引入 Vue -->
    <script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
    <!-- 引入組件庫 -->
    <script src="https://cdn.bootcss.com/element-ui/2.3.2/index.js"></script>
    <script src="https://cdn.bootcss.com/element-ui/2.3.2/locale/zh-CN.min.js"></script>
  </body>
</html>

由於依賴是從外部引入的,因此須要告知 webpack 在打包時,依賴的來源。ios

修改 webpack.base.conf.jsgit

module.exports = {
  ...
  externals: {
    vue: 'Vue',
    'element-ui':'ELEMENT'
  }
}

再一次打包,確實能極大的壓縮了打包的體積,從 700k 驟減至 130k:

可是隨之而來的就有問題了:

明明我在本地開發,可是因爲引入了線上的生產版本的 vue 文件,所以 vue-dev-tools 就不能進行調試。

所以,咱們須要再次調整一下 webpack 的配置,webpack.base.conf.js,並且 webpack 注入的 js 老是在最後面的,所以,咱們須要 html-webpack-include-assets-plugin 幫忙在注入 app.js 後,再注入相對應的組件庫 :

const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin')

const externals = {
  // 由於打包時,還沒注入,因此這裏要去掉。
  // 'element-ui':'ELEMENT'
}
// 生產環境中使用生產環境的 vue
// 開發環境繼續使用本地 node_modules 中的 vue
if (process.env.NODE_ENV === 'production') {
  externals['vue'] = 'Vue'
  // 如發現打包時依舊將 element-ui 打包進入 vendor,能夠在打包時將其加入外部依賴。
  externals['element-ui'] = 'ELEMENT'
}
// 生產環境默認注入 vue 
// 開發環境中不注入
const defaultJS = process.env.NODE_ENV === 'production' ? [{ path: 'https://cdn.bootcss.com/vue/2.4.2/vue.min.js', type: 'js' }] : []
const plugins = [
  new HtmlWebpackIncludeAssetsPlugin({
      assets: defaultJS.concat([
        { path: 'https://cdn.bootcss.com/element-ui/2.3.2/index.js', type: 'js' },
        { path: 'https://cdn.bootcss.com/element-ui/2.3.2/locale/zh-CN.min.js', type: 'js' },
      ]),
      // 是否在 webpack 注入的 js 文件後新增?true 爲 append, false 爲 prepend。
      // 生產環境中,這些 js 應該先加載。
      append: process.env.NODE_ENV !== 'production',
      publicPath: '',
    })
]

module.exports = {
  ...
  externals,
  plugins,
  ...
}

OK,這時候,既能兼顧打包後的體積大小,也能在開發模式中使用 vue-dev-tool 進行調試。

DLL - 提升打包速度

常常打包的前端會發現,不少時候,咱們爲了修復某些bug(如 promise 在 ie Safari 下的 bug),而新引入了一個 polyfill,然而,打包完後發現,vendor 的 hash 值變了,而整個 vendor 只新加了一個 es6-promise 的依賴,可是付出的代價就是,須要拋棄以前打包好的 vendor,用戶從新訪問時,須要再一次拉取一個全新的 vendor,這個代價就有點大了。

這時候,使用 dllPlugin 打包就有優點了。它能夠將一些基礎依賴模塊統一先打包起來,當正式打包時,則能夠略過這些模塊,再也不重複打包進去 vendor,提升打包速度的同時也能減小 vendor 的體積。

如,後臺管理系統基礎模塊基本有如下幾個:

  • axios: ajax 請求。
  • vuex: 全局狀態管理。
  • js-cookie: 前端處理 cookie。
  • vue-router: 路由管理。

這四個基礎模塊幾乎是必須的,那麼能夠先提取出來。

step 1 打包基礎模塊

先在 build 文件夾下新建一個用於打包 dll 的配置文件 webpack.dll.conf.js

const webpack = require('webpack');
const path = require('path');
const vueLoaderConfig = require('./vue-loader.conf')
const utils = require('./utils')

function resolve(dir) {
    return path.join(__dirname, '..', dir)
}

const vendor = [
    // 'vue/dist/vue.runtime.esm.js', // 因爲 vue 在生產環境中使用的是 cdn 引入,因此也無需提早打包進 dll
    // 'raven-js', // 前端監控,若無此需求,能夠忽略。
    'es6-promise', // 修復 promise 中某些 bug。
    'vue-router',
    'js-cookie',
    'axios',
    'vuex',
];

const webpackConfig = {
    context: __dirname,
    output: {
        path: path.join(__dirname, '../static/js/'),
        filename: '[name].dll.js',
        library: '[name]_[hash]',
    },
    entry: {
        vendor
    },
    plugins: [
        new webpack.DllPlugin({
            context: __dirname,
            path: path.join(__dirname, '.', '[name]-manifest.json'),
            name: '[name]_[hash]',
        }),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
              warnings: false
            },
            sourceMap: true,
            // parallel: true
        })
    ],
    module: {
        rules: [{
                test: /\.vue$/,
                loader: 'vue-loader',
                options: vueLoaderConfig
            },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    name: utils.assetsPath('img/[name].[hash:7].[ext]')
                }
            },
            {
                test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    name: utils.assetsPath('media/[name].[hash:7].[ext]')
                }
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
                }
            }
        ]
    }
};

module.exports = webpackConfig

而後在 package.json 中加入一條命令:

{
    "scripts": {
        ...
        "build:dll": "webpack --config build/webpack.dll.conf.js",
        ...
    }
}

執行 yarn build:dll 或者 npm run build:dll 便可完成打包 dll。執行完成後:

yarn build:dll
yarn run v1.5.1
$ webpack --config build/webpack.dll.conf.js
Hash: f6894dff019b2e0734af
Version: webpack 3.10.0
Time: 1295ms
         Asset     Size  Chunks             Chunk Names
vendor.dll.js  62.6 kB       0  [emitted]  vendor
   [8] dll vendor 12 bytes {0} [built]
    + 32 hidden modules
✨  Done in 1.89s.

同時,能夠在 build 目錄下,找到各個模塊對應關係文件 vendors-manifest.jsonstatic/js 下的 vendor.dll.js

step 2 頁面中引入 vendor

打包後的 dll 文件須要手動在 index.html 引入:

<div id="app"></div>
<!-- built files will be auto injected -->
<script src="static/js/vendors.dll.js"></script>
step 3 告訴 webpack 使用 dllPlugin 進行打包

修改 build/webpack.prod.conf.js:

module.exports = {
    plugins: [
        ...
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./vendor-manifest.json')
        }),
        ...
    ]
}

再次打包:

$ yarn build:report
yarn run v1.5.1
$ npm_config_report=true node build/build.js
Hash: b4ff51852866ed865cfd
Version: webpack 3.10.0
Time: 6532ms
                                              Asset       Size  Chunks             Chunk Names
         static/js/manifest.42b9584a653aec2b9c5e.js     1.5 kB       5  [emitted]  manifest
                         static/img/404.a57b6f3.png    98.1 kB          [emitted]
                static/js/1.9e4133a25808e2101dd3.js       1 kB       1  [emitted]
                static/js/2.2a8a8e01c51473fab882.js    4.34 kB       2  [emitted]
           static/js/vendor.c7b076ef3341d4711402.js    39.4 kB       3  [emitted]  vendor
              static/js/app.6d52c7a5bf1bacb5cc85.js    21.4 kB       4  [emitted]  app
                static/js/0.cbc645864aab28ae8055.js    15.3 kB       0  [emitted]
static/css/app.1b30f8eba210e245a5f96d7bf0d6fb6c.css     7.6 kB       4  [emitted]  app
                                        favicon.ico    67.6 kB          [emitted]
                                         index.html  986 bytes          [emitted]
                            static/js/vendor.dll.js    62.6 kB          [emitted]

  Build complete.

  Tip: built files are meant to be served over an HTTP server.
  Opening index.html over file:// won't work.

發現 vendor 如今只有 40k 的體積,減小了一半的體積,並且打包速度也快了 2s,而相對於最開始的基礎模板,打包速度快了 12s,這是很讓人欣慰。

後記

使用了 cdn 和 dll 打包後,不管是打包速度仍是頁面加載的速度都有很大的提高。所以將這次優化記錄下來,並傳上了 GitHub 中。

相關文章
相關標籤/搜索