為什麼要優化打包?
- 專案越做越大,依賴包越來越多,打包檔案太大
- 單頁面應用首頁白屏時間長,使用者體驗差
我們的目的
- 減小打包後的檔案大小
- 首頁按需引入檔案
- 優化 webpack 打包時間
優化方式
1、 按需載入
1.1 路由元件按需載入
const router = [
{
path: '/index',
component: resolve => require.ensure([], () => resolve(require('@/components/index')))
},
{
path: '/about',
component: resolve => require.ensure([], () => resolve(require('@/components/about')))
}
]
複製程式碼
1.2 第三方元件和外掛。按需載入需引入第三方元件
// 引入全部元件
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
// 按需引入元件
import { Button } from 'element-ui'
Vue.component(Button.name, Button)
複製程式碼
1.3 對於一些外掛,如果只是在個別元件中用的到,也可以不要在 main.js 裡面引入,而是在元件中按需引入
// 在main.js引入
import Vue from vue
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
// 按元件按需引入
import { Vuelidate } from 'vuelidate'
複製程式碼
2、優化 loader 配置
- 優化正則匹配
- 通過 cacheDirectory 選項開啟快取
- 通過 include、exclude 來減少被處理的檔案。
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory',
include: [resolve('src')]
}
]
}
複製程式碼
3、優化檔案路徑——省下搜尋檔案的時間
- extension 配置之後可以不用在 require 或是 import 的時候加副檔名,會依次嘗試新增副檔名進行匹配。
- mainFiles 配置後不用加入檔名,會依次嘗試新增的檔名進行匹配
- alias 通過配置別名可以加快 webpack 查詢模組的速度。
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
複製程式碼
4、生產環境關閉 sourceMap
- sourceMap 本質上是一種對映關係,打包出來的 js 檔案中的程式碼可以對映到程式碼檔案的具體位置,這種對映關係會幫助我們直接找到在原始碼中的錯誤。
- 打包速度減慢,生產檔案變大,所以開發環境使用 sourceMap,生產環境則關閉。
5、程式碼壓縮
- UglifyJS: vue-cli 預設使用的壓縮程式碼方式,它使用的是單執行緒壓縮程式碼,打包時間較慢
- ParallelUglifyPlugin: 開啟多個子程式,把對多個檔案壓縮的工作分別給多個子程式去完成
兩種方法使用如下:
plugins: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: true,
parallel: true
}),
new ParallelUglifyPlugin({
//快取壓縮後的結果,下次遇到一樣的輸入時直接從快取中獲取壓縮後的結果並返回,
//cacheDir 用於配置快取存放的目錄路徑。
cacheDir: '.cache/',
sourceMap: true,
uglifyJS: {
output: {
comments: false
},
compress: {
warnings: false
}
}
})
]
複製程式碼
打包速度和打包後的檔案大小啊對比
方法 | 檔案大小 | 打包速度 |
---|---|---|
不用外掛 | 14.6M | 32s |
UglifyJsPlugin | 12.9M | 33s |
ParallelUglifyPlugi | 7.98M | 17s |
6、提取公共程式碼
- 相同資源重複被載入,浪費使用者流量,增加伺服器成本。
- 每個頁面需要載入的資源太大,導致網頁首屏載入緩慢,影響使用者體驗。
webpack3 使用 CommonsChunkPlugin 的實現:
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function(module, count) {
console.log(module.resource, `引用次數${count}`)
//"有正在處理檔案" + "這個檔案是 .js 字尾" + "這個檔案是在 node_modules 中"
return module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, './node_modules')) === 0
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
chunks: 'initial',
minChunks: 2
})
]
複製程式碼
webpack4 使用 splitChunks 的實現:
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
priority: 1, //新增權重
test: /node_modules/, //把這個目錄下符合下面幾個條件的庫抽離出來
chunks: 'initial', //剛開始就要抽離
minChunks: 2 //重複2次使用的時候需要抽離出來
},
common: {
//公共的模組
chunks: 'initial',
minChunks: 2
}
}
}
}
}
複製程式碼
7、CDN 優化
- 隨著專案越做越大,依賴的第三方 npm 包越來越多,構建之後的檔案也會越來越大。
- 再加上又是單頁應用,這就會導致在網速較慢或者伺服器頻寬有限的情況出現長時間的白屏。
1、將 vue、vue-router、vuex、element-ui 和 axios 這五個庫,全部改為通過 CDN 連結獲取,在 index.html 裡插入 相應連結。
<head>
<link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.0.7/theme-chalk/index.css" />
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>
<script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script>
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script>
<script src="https://cdn.bootcss.com/element-ui/2.6.1/index.js"></script>
<!-- built files will be auto injected -->
</body>
複製程式碼
2、在 webpack.config.js 配置檔案
module.exports = {
···
externals: {
'vue': 'Vue',
'vuex': 'Vuex',
'vue-router': 'VueRouter',
'element-ui': 'ELEMENT',
'Axios':'axios'
}
},
複製程式碼
3、解除安裝依賴的 npm 包,npm uninstall axios element-ui vue vue-router vuex
4、修改 main.js 檔案裡之前的引包方式
// import Vue from 'vue'
// import ElementUI from 'element-ui'
// import 'element-ui/lib/theme-chalk/index.css'
// import VueRouter from 'vue-router'
import App from './App.vue'
import routes from './router'
import utils from './utils/Utils'
Vue.use(ELEMENT)
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'hash', //路由的模式
routes
})
new Vue({
router,
el: '#app',
render: h => h(App)
})
複製程式碼
8、使用 HappyPack 多程式解析和處理檔案
- 由於執行在 Node.js 之上的 Webpack 是單執行緒模型的,所以 Webpack 需要處理的事情需要一件一件的做,不能多件事一起做。
- HappyPack 就能讓 Webpack 把任務分解給多個子程式去併發的執行,子程式處理完後再把結果傳送給主程式。
- HappyPack 對 file-loader、url-loader 支援的不友好,所以不建議對該 loader 使用。
使用方法如下:
- HappyPack 外掛安裝: npm i -D happypack
- webpack.base.conf.js 檔案對 module.rules 進行配置
module: {
rules: [
{
test: /\.js$/,
use: ['happypack/loader?id=babel'],
include: [resolve('src'), resolve('test')],
exclude: path.resolve(__dirname, 'node_modules')
},
{
test: /\.vue$/,
use: ['happypack/loader?id=vue']
}
]
}
複製程式碼
- 在生產環境 webpack.prod.conf.js 檔案進行配置
const HappyPack = require('happypack')
// 構造出共享程式池,在程式池中包含5個子程式
const HappyPackThreadPool = HappyPack.ThreadPool({ size: 5 })
plugins: [
new HappyPack({
// 用唯一的識別符號id,來代表當前的HappyPack是用來處理一類特定的檔案
id: 'babel',
// 如何處理.js檔案,用法和Loader配置中一樣
loaders: ['babel-loader?cacheDirectory'],
threadPool: HappyPackThreadPool
}),
new HappyPack({
id: 'vue', // 用唯一的識別符號id,來代表當前的HappyPack是用來處理一類特定的檔案
loaders: [
{
loader: 'vue-loader',
options: vueLoaderConfig
}
],
threadPool: HappyPackThreadPool
})
]
複製程式碼
總結
- 比較實用的方法: 按需載入,優化loader配置,關閉生產環境的sourceMap,CDN優化。
- vue-cli已做的優化: 程式碼壓縮,提取公共程式碼,再優化空間不大。
- 根據專案實際需要和自身開發水平選擇優化方法,必須避免因為優化產生bug。