#### 縮小文件搜索范圍
* 優化loader配置:loader對文件的轉換是很耗時的,使用include或exclude告訴webpack哪些文件需要搜索、哪些不需要搜索。
* 優化resolves.modules配置:告訴webpack第三方模塊和自己常用的模塊在哪,讓webpack直接去訪問而不是一層層尋找(文件夾級別)。
* 優化resolve.alias配置:通過別名將路徑映射成一個新的路徑(多用于第三方模塊)。`alias: {'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js')}`。如果不設置,那么會從`node_modules`開始一直尋找和層層遍歷,直到找到`react.min.js`(文件級別)。
* 優化resolve.extensions配置:沒有后綴時,webpack會嘗試添加后綴去詢問文件是否存在,所以這個配置也會影響性能,最好不要使用或者配置項要盡可能少,出現頻率較高的后綴要放在最前面(最好是import時刻使用后綴而不是讓webpack幫你添加)。
* 優化module.noParse:讓webpack忽略對部分沒采用模塊化的文件的遞歸解析處理(該模塊要求能直接運行在瀏覽器上的,因為webpack不會對它們轉碼)。`noParse: [/react\.min\.js$/]`。
#### 使用DllPlugin
dll文件叫做動態鏈接庫,里面可以包括其他模塊或數據。在webpack中,將一些第三方的模塊做成dll形式,只打包一次,之后直接從dll中讀取(除非這個模塊的代碼有更改了)。該模塊被打包成了`react.dll.js`,對外暴露全局的`_dll_react`。
```
//webpack_dll.config.js
entry: 'react',
output: {
filename: '[name].bundle.js',
path: __dirname,
library: '_dll_[name]'
},
plugins: [new DllPlugin({
name: '_dll_[name]',
path: path.join(__dirname, '[name].manifest.json')
})]
//運行webpack --config webpack_dll.config.js來構建dll
//只需運行這一次
//之后按照之前的正常構建項目即可
```
```
//webpack.config.js
plugins: [
new DllReferencePlugin({
manifest: require('./react.manifest.json')
})
]
```
你會發現打包后多出了`react.dll.js`和`react.manifest.json`兩個文件,一個是包含了react的模塊內部通過`_dll_react`將自己暴露在全局中供其他模塊使用。一個是描述文件,用于描述在動態鏈接庫中包含了哪些文件(路徑、id等)。
#### 使用HappyPack
HappyPack允許多線程處理任務(在js中就是多進程)。
```
module: {
rules: [{
test: /\.js$/,
use: ['happypack/loader?id=babel']
}]
},
plugins: [
new HappyPack({
id: 'babel',
loaders: [{
loader: 'babel-loader',
options: {
presets: ['es2015', 'stage-2']
}
}]
})
]
```
原理:webpack最耗時的就是loader對文件的轉換操作,因為要轉換的文件數據量巨大,而且這些轉換操作都只能一個一個處理。HappyPack的核心原理就是將這部分任務分解到多個進程中去并行處理,從而減少總的構建時間。
#### 使用ParallelUglifyPlugin
由于壓縮代碼時需要先將代碼轉換成AST語法樹,再去根據規則分析和處理AST,導致計算量巨大。使用ParallelUglifyPlugin并行處理,同HappyPack。
#### 自動刷新
文件監聽的原理:定時獲取文件的最后編輯時間,每次都存下最新的最后編輯時間,如果發現當前獲取的時間和最后一次保存的編輯時間不一致,說明文件有更改。通過`watchOptions.poll`配置定時檢查的周期。當文件變化時,不是立即告訴監聽者,而是先緩存起來,收集一段時間的變化后(`watchOptions.aggregateTimeout`)在一次性告訴監聽者,以防止高頻修改文件導致重復構建的卡死。至于多入口文件,則是從入口開始遞歸解析它所依賴的文件,將它們都加入監聽列表中。
優化文件監聽:減少監聽文件數量-->`watchOptions.ignored = /node_modules/`;增大`aggregateTimeout`的值,減小`poll`的值。
自動刷新原理:借助瀏覽器的接口進行刷新;向頁面中注入代理客戶端代碼,通過代理客戶端去刷新整個頁面;將頁面裝進一個iframe中,通過iframe去看到最新效果。
優化自動刷新:`devServer.inline`代表是否向chunk中注入代理客戶端,在開啟時devServer會向每個chunk注入代理客戶端的代碼導致構建緩慢。其實要完成自動刷新,一個頁面只需要一個代理客戶端,devServer之所以會為每個chunk注入,是因為它不知道某個頁面依賴了哪些chunk。所以,關閉inline來提高性能。
```
//html
//自動刷新實際上是嵌入了iframe
<iframe id="iframe">
#document
<html>
<head>...</head>
<body>
<div id="app">
<h1 data-reactroot>hello, webpack</h1>
</div>
...
</body>
</html>
</iframe>
```
`webpack-dev-server --inline false
`
#### 模塊熱替換
不刷新整個頁面而是只更新改變的模塊。
原理:與自動刷新原理類似,都是往頁面中注入一個代理客戶端來連接devServer和網頁,不同在于模塊熱替換的獨特的模塊替換機制。`webpack-dev-server -hot`
優化:使用`plugins: [new NamedModulesPlugin()]`在控制臺上輸出熱替換的模塊名字(不使用該插件的話則默認是輸出id)。同文件監聽,需要忽略node_modules目錄來減少監聽數量(熱替換使用inline: false,也就是說只能減少文件監聽數量)。
#### 區分環境
```
process.env.NODE_ENV === 'production'
plugins: [
new DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
]
//之所以使用JSON序列化是因為環境變量值需要一個由雙引號包裹的字符串。'"production"'
```
#### Tree Shaking
提出js中用不上的死代碼,要求代碼必須采用es6的模塊化語法
```
.babelrc
{
"presets": [[
"env", {
"modules": false
//告訴babel關閉es6的模塊轉換功能以保留原本的es6模塊化語法
}
]]
}
```
```
//utils.js
export functionA
export functionB
//main.js
import {functionA} from './utils';
functionA();
//TreeShaking后
//utils
export functionA
```
#### 提取公共代碼
```
new CommonsChunkPlugin({
chunks: ['a', 'b'],
name: 'common'
})
new CommonsChunkPlugin({
chunks: ['common', 'base'],
name: 'base'
})
//先將a和b提取成common
//再將common和base提取成base
//也就是說,CommonsChunk可以使用多次
```
CommonsChunkPlugin提供一個選項minChunks,表示文件要被提取出來時需要在指定的Chunks中出現的最小次數。假如`minChunks=2; chunks=['a', 'b', 'c', 'd']`,代表:任何一個文件只要在abcd這四個chunk的兩個以上的chunk中都出現過,則該文件就會被提取出來。比如:e在a、b中均出現過,則e被提取。
#### 分割代碼以按需加載
坑請參考配置里面的關于publicPath那一節。
```
output: {
filename: '[name].js',
chunkFilename: '[name].js' //chunkFilename中的[name]與[id]值相同
}
//main
$scope.click = () => import('./common/async.js').then(data => console.log(data));
```
可以在自己的js中為生成的按需加載的模塊定義名字
```
import (/* webpackChunkName: "page-login" */ './common/async.js').then(...)
//最終生成
page-login.js而不是0.js
```
#### Scope Hoisting
作用域提升:減少函數聲明語句、減少運行時創建的函數作用域。其原理就是分析模塊間的依賴關系,盡可能將被打散的模塊合并到一個函數中(前提是不能造成代碼冗余)。
同TreeShaking,要求源碼必須采用es6模塊化語句。