你不知道的 Webpack 性能優化配置

做者:心靜止水(前端時空)javascript

公衆號「前端時空」每日一題活動
回覆「1」看面試題 | 回覆「2」看答案

目錄

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

開發環境性能優化

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

HMR

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

做用:一個模塊發生變化,只會從新打包這一個模塊,而不是打包全部模塊,極大的提高了構建速度html

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

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
module.exports = {  
    mode: 'development', // 'production' 
    devtool: 'eval-source-map' // 'source-map'
}複製代碼

生產環境性能優化

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

oneOf

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

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

cache(緩存)

  1. babel 緩存
    讓第二次打包構建速度更快
module.exports = {  
    module: {    
        rules: [     
            {        
                test: /\.js$/,        
                exclude: /node_modules/,        
                loader: 'babel-loader',        
                options: {          
                    presets: {        // 開啓 babel 緩存 // 第二次構建時,會讀取以前的緩存 
                        cacheDirectory: true  
                    }      
                }     
             }    
        ]  
    }
}複製代碼
  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:去除無用的代碼java

前提:1. 必須使用 ES6 模塊化;2. 開啓 production 環境
做用:減小代碼體積

在 package.json 中配置:node

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

code split(代碼分割)

  1. 多入口形式:
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'
}複製代碼
  1. optimization:
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'
}複製代碼
  1. 「某個文件的單獨打包」
// import動態導入語法:能將某個文件單獨打包成一個 chunk// 此處的註釋能夠命名打包後文件名import(/* webpackChunkName: 'test' */ './test.js')  .then(() => {})  .catch(() => {})複製代碼

懶加載 和 預加載

懶加載:當文件須要時才加載react

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

正常加載能夠認爲是並行加載(同一時間加載多個文件)webpack

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

PWA

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

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

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


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

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

多進程打包

插件:npm i thread-loader -D
進程啓動大概爲 600ms,進程通訊也有開銷。只有工做消耗品時間比較長,才須要多進程打包。

module.exports = {  
    module: {    
    rules: [      {        
        test: /\.js$/,        
        exclude: /node_modules/,        
        use: [          
            // 開啓多進程打包 
            'thread-loader',          {            
             loader: 'babel-loader',            
                options: {              
                    presets: []            
                }          
              }        
            ]      
         }    
       ]  
    }
}複製代碼

externals

  1. 設置拒絕打包的庫
module.exports = {  
    externals: {    
        // 忽略庫名 --> npm包名 
        jquery: 'jQuery'  
    }
}複製代碼
  1. 在入口 index.html 引入 CDN
    <script src="xxx"></script>

dll

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

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'
}複製代碼
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'
}
複製代碼
相關文章
相關標籤/搜索