打包優化策略
提取公共程式碼與第三方程式碼
將多個入口重複載入的公共資源提取出來
- 相同的資源被重複的載入,浪費使用者的流量和伺服器的成本;
- 每個頁面需要載入的資源太大,導致網頁首屏載入緩慢,影響使用者體驗。 如果能把公共程式碼抽離成單獨檔案進行載入能進行優化,可以減少網路傳輸流量,降低伺服器成本
- 在webpack4.0 optimization.splitChunks替代了CommonsChunkPlugin
基礎配置
optimization: {
splitChunks: {
// 預設打包node_modules到venders.js
chunks: 'all'
},
// 將webpack執行時生成程式碼打包到runtime.js
runtimeChunk: true
},
複製程式碼
webpack.base.config.js提取配置
optimization: {
// runtimeChunk: {
// name: "manifest"
// },
splitChunks: {
cacheGroups: {
commons: {
chunks: 'initial',
minChunks: 2,
maxInitialRequests: 5,
minSize: 0
},
vendor: { // 將第三方模組提取出來
test: /node_modules/,
chunks: 'initial',
name: 'vendor',
priority: 10, // 優先
/* 為此快取組建立塊時,告訴webpack忽略splitChunks.minSize, splitChunks.minChunks, splitChunks.maxAsyncRequests and splitChunks.maxInitialRequestss選項。*/
enforce: true
}
}
}
}
複製程式碼
optimization引數介紹:
- webpack根據下述條件自動進行程式碼塊分割:
- 新程式碼塊可以被共享引用,OR這些模組都是來自node_modules資料夾裡面
- 新程式碼塊大於30kb(min+gziped之前的體積)
- 按需載入的程式碼塊,最大數量應該小於或者等於5
- 初始載入的程式碼塊,最大數量應該小於或等於3
optimization: {
splitChunks: {
chunks: "initial", // 程式碼塊型別 必須三選一: "initial"(初始化) | "all"(預設就是all) | "async"(動態載入)
minSize: 0, // 最小尺寸,預設0
minChunks: 1, // 最小 chunk ,預設1
maxAsyncRequests: 1, // 最大非同步請求數, 預設1
maxInitialRequests: 1, // 最大初始化請求書,預設1
name: () => {}, // 名稱,此選項課接收 function
cacheGroups: { // 快取組會繼承splitChunks的配置,但是test、priorty和reuseExistingChunk只能用於配置快取組。
priority: "0", // 快取組優先順序 false | object |
vendor: { // key 為entry中定義的 入口名稱
chunks: "initial", // 必須三選一: "initial"(初始化) | "all" | "async"(預設就是非同步)
test: /react|lodash/, // 正則規則驗證,如果符合就提取 chunk
name: "vendor", // 要快取的 分隔出來的 chunk 名稱
minSize: 0,
minChunks: 1,
enforce: true,
reuseExistingChunk: true // 可設定是否重用已用chunk 不再建立新的chunk
}
}
}
}
複製程式碼
在滿足下述所有條件時,那些從相同程式碼塊和快取組來的模組,會形成一個新的程式碼塊(譯註:比如,在滿足條件下,一個vendoer可能會被分割成兩個,以充分利用並行請求效能)。
有四個選項可以用於配置這些條件:
- minSize(預設是30000):形成一個新程式碼塊最小的體積
- minChunks(預設是1):在分割之前,這個程式碼塊最小應該被引用的次數(譯註:保證程式碼塊複用性,預設配置的策略是不需要多次引用也可以被分割)
- maxInitialRequests(預設是3):一個入口最大的並行請求數
- maxAsyncRequests(預設是5):按需載入時候最大的並行請求數。
懶載入(按需載入)
是一種很好的優化網頁或應用的方式。這種方式實際上是先把你的程式碼在一些邏輯斷點處分離開,然後在一些程式碼塊中完成某些操作後,立即引用或即將引用另外一些新的程式碼塊。這樣加快了應用的初始載入速度,減輕了它的總體體積,因為某些程式碼塊可能永遠不會被載入。
lazy.js
export default 'lazy loader';
複製程式碼
main.js 當點選按鈕時 再載入lazy.js 輸出裡面內容
let output = () => {
import('./lazy').then(module => {
console.log(module.default);
});
};
ReactDOM.render(
<div>
<button onClick={output}>點選</button>
</div>,
document.querySelector('#root')
)
複製程式碼
vue中懶載入
const Login = () => import(/* webpackChunkName: "login" */'./login')
new VueRouter({
routes: [
{ path: '/login', component: Login }
]
})
複製程式碼
react中懶載入
babel-plugin-syntax-dynamic-import plugin. This is a syntax-only plugin, meaning Babel won’t do any additional transformations. The plugin simply allows Babel to parse dynamic imports so webpack can bundle them as a code split. Your .babelrc should look something like this:{
"presets": [
"react"
],
"plugins": [
"syntax-dynamic-import"
]
}
react-loadable is a higher-order component for loading components with dynamic imports. It handles all sorts of edge cases automatically and makes code splitting simple! Here’s an example of how to use react-loadable:import Loadable from 'react-loadable';
import Loading from './Loading';
const LoadableComponent = Loadable({
loader: () => import('./Dashboard'),
loading: Loading,
})
export default class LoadableDashboard extends React.Component {
render() {
return <LoadableComponent />;
}
}
複製程式碼
開啟Scope Hoisting
在webpack4中當mode為production時預設開啟了Scope Hoisting 可以讓webpack打包出來的程式碼檔案更小、執行更快,它又譯作“作用域提升”。
好處: • 程式碼體積更小,因為函式申明語句會產生大量程式碼; • 程式碼在執行時因為建立的函式作用域更少了,記憶體開銷也隨之變小
scope.js
export default 'scope hoisting'
複製程式碼
main1.js
import scope from './scope';
console.log(scope);
複製程式碼
webpack3 配置scope Hoisting
new webpack.optimize.ModuleConcatenationPlugin()
複製程式碼
開啟scope hoisting後的程式碼
// CONCATENATED MODULE: ./src/scope.js
/* harmony default export */ var scope = ('scope hoisting');
// CONCATENATED MODULE: ./src/main1.js
console.log(scope);
複製程式碼
ES6的靜態模組分析,分析出模組之間的依賴關係,儘可能地把模組放到同一個函式中。
同時,考慮到 Scope Hoisting 依賴原始碼需採用 ES6 模組化語法,還需要配置 mainFields。因為大部分 Npm 中的第三方庫採用了 CommonJS 語法,但部分庫會同時提供 ES6 模組化的程式碼,為了充分發揮 Scope Hoisting 的作用,需要增加以下配置
mainFields用於配置第三方模組使用那個入口檔案
module.exports = {
resolve: {
// 針對 Npm 中的第三方模組優先採用 jsnext:main 中指向的 ES6 模組化語法的檔案
mainFields: ['jsnext:main', 'browser', 'main']
},
};
複製程式碼
對於採用了非 ES6 模組化語法的程式碼,Webpack 會降級處理不使用 Scope Hoisting 優化