Webpack 性能優化,這些你都知道?

前端網紅集結號,傳遞一線全棧技術,帶你穿越前端時空。

公眾號「前端時空」最近推出每日一題活動,

回覆「0」進入交流群,

回覆「1」看每日一題,

回覆「2」看答案解析


Webpack 性能優化,這些你都知道?

Webpack 性能優化,這些你都知道?


目錄

  1. 開發環境性能優化
  2. 生產環境性能優化

開發環境性能優化

  • 優化打包構建速度
    • HMR
  • 優化代碼調試
    • source-map


HMR

概念:「HMR:」 hot module replacement 熱模塊替換 / 模塊熱替換

作用:一個模塊發生變化,只會重新打包這一個模塊,而不是打包所有模塊,極大的提升了構建速度

  1. 樣式文件:可以使用 HMR 功能:因為 style-loader 內部已經實現
  2. js 文件:默認不能使用 HMR 功能:開啟需要添加支持 HMR 功能的 js 代碼,且只能處理 「非入口 js 文件」(入口文件將其它文件全部引入,若添加,會導致全部重新加載)
  3. html 文件:默認不能使用 HMR 功能,同時會導致 「html 文件不能熱更新」
    解決:修改 「entry」 入口,將 html 文件引入
<code>module.exports = {
// 引入html,解決熱更新的問題
entry: ['./src/js/index.js', './src/index.html'],
devServer: {
// 開啟 HMR 功能
// 當修改了 webpack 配置,新配置要想生效,必須重啟服務
hot: true
}
}
/<code>


source-map

概念:一種提供源代碼構建後代碼映射技術(如果構建後代碼出錯了,可以通過映射追蹤到源代碼錯誤)

  • 參數:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
    • source-map:外部(錯誤代碼的準確信息 和 位置)
    • inline-source-map:內聯(只生成一個內聯 source-map)(錯誤代碼的準確信息 和 位置)
    • hidden-source-map:外部(直接生成 .map 文件)(不能追蹤源代碼錯誤,只能提示到構建後代碼的錯誤位置)
    • eval-source-map:內聯(每一個文件都生成對應的 source-map,都在 eval)(錯誤代碼的準確信息 和 位置)
    • nosources-source-map:外部(錯誤代碼的準確信息,沒有源代碼信息)
    • cheap-source-map:外部(錯誤代碼的準確信息 和 位置,但只能精確到行)
    • cheap-module-source-map:外部(錯誤代碼的準確信息 和 位置,會將 loader 的 source-map 加入)
  • 開發環境:速度快,調試更友好。eval-source-map / eval-cheap-module-source-map
    (vue 和 react 腳手架中默認使用:eval-source-map)
    • 速度快:(eval > inline > cheap > ...)
      eval-cheap-source-map、
      eval-source-map、
    • 調試友好:
      source-map、
      cheap-module-source-map、
      cheap-source-map
  • 生產環境:源代碼要不要隱藏?調試要不要更友好?source-map / cheap-module-source-map
    內聯會讓代碼體積變大,所以在生產環境中只會只用 「外部 source-map」
    nosources-source-map、
    hidden-source-map
<code>module.exports = {
mode: 'development', // 'production'
devtool: 'eval-source-map' // 'source-map'
}/<code>

生產環境性能優化

  • 優化打包構建速度
    • oneOf
    • babel 緩存
    • 多進程打包
    • externals
    • dll
  • 優化代碼運行的性能
    • 緩存(hash -> chunkhash -> contenthash)
    • tree shaking
    • code split
    • 懶加載/預加載
    • PWA

oneOf

oneOf:避免了每一個文件都要被 loader 過一次 注:不能有兩個配置處理同一種類型文件

<code>module.exports = {
module: {
rules: [
{
test: /\\.js$/,
exclude: /node_modules/,
//優先執行
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{
// 以下 loader 只會匹配一個
oneOf: [
...,
{},
{}
]
}
]
}
}/<code>

cache(緩存)

  1. babel 緩存
    讓第二次打包構建速度更快
<code>module.exports = {
module: {
rules: [
{
test: /\\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
// 開啟 babel 緩存
// 第二次構建時,會讀取之前的緩存
cacheDirectory: true
}
}
]
}
}
/<code>
  1. 文件資源緩存
    讓代碼上線運行緩存更好使用
  • hash:每次 webpack 構建時會生成一個唯一的 hash 值
    問題:因為 js 和 css 是同時使用一個 hash 值(如果重新打包會導致所有文件緩存都失效)
  • chunkhash:根據 chunk 生成 hash 值。如果打包來源於同一個 chunk,那麼 hash 值就一樣
    問題:js 和 css 的 hash 值還是一樣的
    因為 css 是在 js 文件中被引入的,所以同屬於一個 chunk
  • 「contenthash」
    :根據文件的內容生成 hash 值。不同文件的 hash 值移動不一樣

tree shaking(搖樹)

tree shaking:去除無用的代碼

前提:1. 必須使用 ES6 模塊化;2. 開啟 production 環境
作用:減少代碼體積
在 package.json 中配置:

  • "sideEffects": false 所有的代碼都沒有副作用(都可以進行 tree shaking)
    問題:可能會把 css / @babel/polyfill (副作用)文件幹掉
  • sideEffects: ["*.css", "*.less"]

code split(代碼分割)

  1. 多入口形式:
<code>module.exports = {
entry: {
// 多入口
main: './src/js/index.js',
test: './src/js/test.js'
},
output: {
// [name]: 取文件名
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build')
},

mode: 'production'
}
/<code>
  1. optimization:
<code>module.exports = {
entry: './src/js/index.js',,
output: {
filename: 'js/built.[contenthash:10].js',
path: resolve(__dirname, 'build')
},
// 可以將 node_modules 中的代碼單獨打包一個chunk最終輸出
optimization: {
splitChunks: {
chunks: 'all'
}
}
mode: 'production'
}
/<code>
  1. 「某個文件的單獨打包」
<code>// import動態導入語法:能將某個文件單獨打包成一個 chunk
// 此處的註釋可以命名打包後文件名
import(/* webpackChunkName: 'test' */ './test.js')
.then(() => {})
.catch(() => {})/<code>

懶加載 和 預加載

懶加載:當文件需要時才加載


預加載 prefetch:會在使用前,提前加載 js 文件。等其他資源加載完畢,瀏覽器空閒了,在偷偷加載資源


正常加載可以認為是並行加載(同一時間加載多個文件)

<code>// import動態導入語法:能將某個文件單獨打包成一個 chunk
// webpackChunkName 此處的註釋可以命名打包後文件名,webpackPrefetch 預加載
import(/* webpackChunkName: 'test', webpackPrefetch: true */ './test.js')
.then(() => {})
.catch(() => {})/<code>

PWA

PWA:漸進式網絡開發應用程序(離線可訪問)

插件:workbox --> npm i workbox-webpack-plugin -D

<code>module.exports = {
plugins: [
new WorkboxWebpackPlugin.GenerateSW({
// 1. 幫助 serviceworker 快速啟動
// 2. 刪除舊的 serviceworker
// 生成一個 serviceworker 配置文件
clientsClaim: true,
skipWaiting: true
})
]
}
/<code>


註冊 serviceworker,並處理兼容性問題

  1. eslint 不認識 window、navigator 等全局變量
    解決:需要修改 package.json 中 eslintConfig 配置
<code>"env": {
"browser": true
}
/<code>
  1. sw 代碼必須運行在服務器上
<code>// 註冊 serviceworker,並處理兼容性問題
if ('serviceworker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceworker
.register('/service-worker.js')
.then(() => {
console.log('sw註冊成功了')
})
.catch(() => {
console.log('sw註冊失敗了')
})
})
}/<code>

多進程打包

插件:npm i thread-loader -D
進程啟動大概為 600ms,進程通信也有開銷。只有工作消耗品時間比較長,才需要多進程打包。

<code>module.exports = {
module: {
rules: [
{
test: /\\.js$/,
exclude: /node_modules/,
use: [

// 開啟多進程打包
'thread-loader',
{
loader: 'babel-loader',
options: {
presets: []
}
}
]
}
]
}
}/<code>

externals

  1. 設置拒絕打包的庫
<code>module.exports = {
externals: {
// 忽略庫名 --> npm包名
jquery: 'jQuery'
}
}
/<code>
  1. 在入口 index.html 引入 CDN

dll

對代碼進行單獨打包,(第三方庫:jQuery,react,vue ...),第二次以後打包時不再打包第三方庫。
webpack.dll.js 文件:
注:運行 webpack 時,默認查找 webpack.config.js,需要運行 webpack.dll.js 文件時,可以通過運行 webpack --config webpack.dll.js 實現運行


<code>const { resolve } = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
// 最終打包生成的[name] --> jquery
// ['jquery'] --> 要打包的庫是 jquery
jquery: ['jquery']
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'dll'),
library: '[name]_[hash]' // 打包的庫裡面向外暴露的內容的名字
},
plugins: [
// 打包生成一個 manifest.json --> 提供和 jQuery 映射
new webpack.DllPlugin({
name: '[name]_[hash]', // 映射庫的暴露的內容名稱
path: resolve(__dirname, 'dll/manifest.json')
})
],
mode: 'produciton'
}
/<code>
<code>const { resolve } = require('path')
const webpack = require('webpack')
module.exports = {
plugins: [
// 告訴webpack哪些庫不參與打包,同時使用名稱改變
new webpack.DllReferencePlugin({
path: resolve(__dirname, 'dll/manifest.json')
}),
// 將某個文件打包輸出,並在html中自動引入
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
],
mode: 'produciton'
}/<code>


分享到:


相關文章: