前言
公司有好幾個項目都有后臺管理系統,為了方便開發,所以選擇了 vue 中比較火的后臺模板 作為基礎模板進行開發。但是,開始用的時候,作者并沒有對此進行優化,到項目上線的時候,才發現,打包出來的文件都十分之大,就一個 vendor 就有 770k 的體積(下圖是基礎模板,什么都沒加打包后的文件信息):
通過 webpack-bundle-analyzer
進行分析可得,體積主要來源于餓了么UI(體積為 500k),因為沒對其進行部分引入拆分組件,導致 webpack 把整個組件庫都打包進去了。其次就是 vue 本身,體積也達到了 80k 之大。
所以,對其進行打包優化,是一件刻不容緩的事情。
優化
優化主要目的有:
解決第一個問題,很多人都會想到資源文件放在 CDN 上就好了,沒錯,這次我們就是通過 CDN 來解決加載問題。
CDN - 提高加載速度
像 vue, element ui 這些比較成熟的框架/組件庫,一般都有免費、高速、公共的 cdn 供開發者使用,鑒于大部分用戶均在國內,所以這次使用了 bootcdn 這個庫。該庫熱門資源比較齊全,各個版本都有,而且國內訪問速度很快,簡直是開發者的福音。
在 index.html
中引入 vue 和 餓了么組件。
<!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 rel="external nofollow" 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 在打包時,依賴的來源。
修改 webpack.base.conf.js
:
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 中的 vueif (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 的體積。
如,后臺管理系統基礎模塊基本有以下幾個:
這四個基礎模塊幾乎是必須的,那么可以先提取出來。
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:dllyarn run v1.5.1$ webpack --config build/webpack.dll.conf.jsHash: f6894dff019b2e0734afVersion: webpack 3.10.0Time: 1295ms Asset Size Chunks Chunk Namesvendor.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.json
和 static/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:reportyarn run v1.5.1$ npm_config_report=true node build/build.jsHash: b4ff51852866ed865cfdVersion: webpack 3.10.0Time: 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 中。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
新聞熱點
疑難解答