webpack 性能優化
開發環境性能優化
優化打包構建速度
HMR優化代碼調試source-map
生產環境性能優化
優化打包構建速度
oneOf babel 緩存 多進程打包 externals dll 優化代碼運行的性能 緩存(hash,chunkhash,contenthash) tree shaking code split 懶加載和預加載 pwa
優化 開發環境 打包構建速度
HMR hot module replacement 熱模塊替換/模塊熱替換
作用:一個模塊發生變化,只會重新打包這一個模塊 而不是重新打包所有,極大提升構建速度
樣式文件: 可以使用HMR功能,因為style-loader內部實現了 js文件: 默認不能使用HMR功能 -->解決:需要修改js代碼,添加支持HMR功能的代碼。注意,HMR功能對js的處理,只能處理非入口js文件的其他文件。
- if(module.hot){
- //一旦module.hot是true,說明開啟HMR功能,讓HMR功能代碼生效
- module.hot.accept('./xxx.js',function(){
- //此方法會監聽print.js文件的變化,一旦發生變化,其他默認不會重新打包構建
- //會執行后面的回調函數
- xxx();
- })
- }
html文件: 默認不能使用HMR功能,同時會導致問題:html文件不能熱更新了 解決:改 entry:['入口js','html'] ,但html文件只有一個,所以不用做HMR功能
- devServer:{
- //項目構建后的目錄
- contentBase: resolve(__dirname,'build'),
- //啟用gzip壓縮
- compress:true,
- //端口號
- port:3000,
- //自動打開瀏覽器
- open:true
- }
優化 開發環境 代碼調試
source-map 一種提供源代碼到構建后代碼映射的技術
如果構建后代碼出錯了,通過映射可以追蹤到錯誤的代碼
- webpack.config.js
- devtools:'source-map'
- //其他 參數 [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
- source-map : 外部
- 錯誤代碼的準確信息和錯誤位置
- inline-source-map : 內聯
- 錯誤代碼的準確信息和錯誤位置
- hidden-source-map : 外部
- 錯誤代碼的錯誤原因 但沒有錯誤位置,不能追蹤到源代碼的錯誤,只能提示到構建后代碼的位置
- eval-source-map : 內聯
- 每一個文件都生成對應的source-map,都在eval
- 錯誤代碼的準確信息和錯誤位置
- nosources-source-map : 外部
- 能找到錯誤代碼的準確信息 但沒有任何源代碼信息
- cheap-source-map : 外部
- 錯誤代碼的準確信息和錯誤位置 只精確到行,不精確到列
- cheap-module-source-map : 外部
- 錯誤代碼的準確信息和錯誤位置
- module 會將 loader 的 source-map加入
- 內聯 和 外部的區別 :
- 1.外部生成了文件但內聯沒有生成
- 2.內聯構建速度更快
開發環境:速度快,調試更友好
- 速度快 eval>inline>cheap>...
- 調試更友好 souce-map>cheap-module-souce-map>cheap-souce-map
- 所以 一般用eval-source-map
生產環境:源代碼要不要隱藏,調試要不要友好?內聯會讓體積編碼,所以一般不用內聯
- nosources-source-map 隱藏源代碼
- hidden-source-map 只隱藏源代碼,會提示構建購代碼錯誤信息
- --> source-map /cheap-module-souce-map
優化生產環境
oneOf
rules里中有許多個loader,這樣會導致每個文件都會被所有的loader過一遍,有些能處理,有些處理不了。所以可以利用oneOf達到以下loader只會匹配到第一個。但需要注意,不能有兩個loader同時處理同一個文件
- webpack.config
- module.exports={
- //....
- module:{
- rule:[
- //正常來講,一個文件只能被一個loader處理
- //當一個文件要被多個loader處理,那么一定要指定loader的執行順序
- // 先執行eslint 再執行babel
- {
- test:/\.js$/,
- exclude:/node_modules/,
- //優先執行
- enforce:'pre',
- loader:'eslint-loader',
- options:{
- fix:true
- }
- },
- {
- oneOf:[
- {
- test: /\.css$/,
- use:[
- ...commonCssLoader
- ]
- },
- {
- test:/\.less$/,
- use:[
- ...commonCssLoader,'less-loader'
- ]
- },
- {
- test:/\.js$/,
- exclude:/node_modules/,
- loader:'babel-loader',
- options:{
- // 預設 :指示babel做怎樣的兼容性處理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加載
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪個版本的瀏覽器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ]
- }
- },
- {
- test:/\.(png|jpg|gif)/,
- loader:'url-loader',
- enModule:true,
- options:{
- limit:8*1024,
- name: '[hash:10].[ext]',
- outputpath:''
- }
- },
- {
- test:/\.html$/,
- loader:'html-loader'
- },
- {
- exclude:/\.(js|less|css|png|jpg|gif)/,
- loader:'file-loader',
- options:{
- name:'[hash:10].[ext]'
- }
- }
- ]
- }
- ]
- },
- }
緩存
1.babel緩存-->第二次打包更快
- {
- test:/\.js$/,
- exclude:/node_modules/,
- loader:'babel-loader',
- options:{
- // 預設 :指示babel做怎樣的兼容性處理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加載
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪個版本的瀏覽器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ],
- //第二次構建時,會讀取之前的緩存
- cacheDirectory: true
- }
- },
2.文件資源緩存-->上線緩存優化
- hash:每次webpack構建會生成一個唯一hash值
- 問題: 因為js和css同時使用一個hash值,如果重新打包,會導致所有緩存失效,可能我卻只改了一個文件,
- chunkhash:根據chunk生成hash值,如果打包來源于同一個chunk,那么hash值也一樣
- 問題:js和css的hash值還是一樣的。
- 原因:css是由js引入的,所以屬于同一個chunk
- contenthash: 根據文件內容生成hash值,
- webpack.config.js
tree shaking
去除應用程序中沒有使用的代碼
- 前提:
- 1.必須使用es6模塊化
- 2.開啟production模式
- 在package.json中配置
- "sideEffects":false 所有代碼都沒有副作用,都可以鏡像tree sharking
- 問題 可能會把css/@babel/polyfille 干掉
- "sideEffects": ["*.css","*.less"] 哪些文件不 tree sharking
code split
1.入口配置
- 單入口 //單頁面應用
- entry:'./src/js/index.js'
- 多入口 //多頁面應用
- entry:{
- index:'./src/js/index.js',
- test:'./src/js/test.js'
- }
2.optimization
- module.exports={
- //...
- // 可以將nodemudules中的代碼單獨打包成一個chunk最終輸出
- // 還可以自動分析多入口chunk中,有沒有公共的文件,如果有會打包成一個單獨的chunk
- optimization:{
- splitChunks:{
- chunks:'all'
- }
- }
- }
3.import 動態導入語法,能將某個文件單獨打包
通過js代碼,讓某個文件被單獨打包成一個chunk,通過注釋可以固定此文件的名稱
- import(/*webpackChunkName: 'xxxName' */'./xx/xxx.js')
- .then(res =>{
- //加載成功
- })
- .catch(()=>{
- //加載失敗
- })
懶加載和預加載
1.懶加載 當文件需要用時才加載
import 動態導入語法
- document.getElementById('btn').onclick = function(){
- import(/*webpackChunkName: 'xxxName' */'./xx/ss.js')
- .then(res=>{
- //干啥干啥
- })
- }
2.預加載 webpackPrefetch:true
./xx/ss.js 已經被加載了,點擊的時候再從緩存中加載,
- document.getElementById('btn').onclick = function(){
- import(/*webpackChunkName: 'xxxName',webpackPrefetch:true */'./xx/ss.js')
- .then(res=>{
- //干啥干啥
- })
- }
正常加載可以認為是并行加載(同一時間加載多個文件) 預加載prefectch 等其他資源加載完畢,瀏覽器空閑了,再偷偷加載資源 兼容性比較差 慎用
PWA 漸進式網絡開發應用程序
網絡離線可訪問
webbox-->webbox-webpack-plugin
- const WebboxWebpackPlugin = require('webbox-webpack-plugin')
- module.exports={
- plugins:[
- new WebboxWebpackPlugin.GenerateSW({
- /*
- 1.幫助 serviceWorker 快速啟動
- 2.刪除舊的 serviceWorker
- 生成一個 serviceWorker 配置文件
- */
- clientsClaim:true,
- skipWaiting:true
- })
- ]
- }
- index.js 中注冊serviceworker
- //處理兼容性
- if('serviceWorker' in navigator){
- window.addEventListener('load',()=>{
- navigator.serviceWorker
- .register('./service-work.js')
- .then(()=>{
- //成功
- })
- .catch(()=>{
- //失敗
- })
- })
- }
- 1.可能會出現問題 eslint不認識window和navigator
- 解決 package.json中eslintConfig中配置
- "env":{
- "browser":true //支持瀏覽器端的變量
- }
- 2. sw代碼必須運行在服務器上
- -->node.js
- --> npm i serve -g
- serve -s build 啟動一個服務器將build下的資源作為靜態資源暴露出去
多進程打包
thread-loader 一般給babel-loader用
但需要注意
進程啟動大約需500ms,進程間通信也有開銷。只有工作消耗時間比較長,才需要多進程打包
- {
- test:/\.js$/,
- exclude:/node_modules/,
- use:[
- //'thread-loader',
- {
- loader:'thread-loader',
- options:{
- workers: 2 //進程2個
- }
- }
- {
- loader:'babel-loader',
- options:{
- // 預設 :指示babel做怎樣的兼容性處理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加載
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪個版本的瀏覽器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ],
- //第二次構建時,會讀取之前的緩存
- cacheDirectory: true
- }
- }
- ]
- },
externals
- module.exports={
- externals:{
- //忽略/拒絕 庫名 -- npm 包名
- //可以在index.html中引入cdn
- }
- }
dll 動態連接
使用dll技術 對某些庫(第三方庫) 進行單獨打包
- 指令 webpack --config webpack.dll.js
- webpack.dll.js
- const {resolve} = require('path')
- const webpack = require('webpack')
- module.exports = {
- entry:{
- //最終打包生成的[name]-->jquery
- //['jquery']-->要打包的庫是jquery
- jquery:['jquery']
- },
- ouput:{
- filename:'[name].js',
- path:resolve(__dirname,'dll'),
- library:'[name]_[hash]' //打包的庫里面向外暴露出去的內容叫什么名字
- },
- plugin:[
- new webpacl.DllPlugin({
- //打包生成一個manifest.json --> 提供和jquery映射
- name: '[name]_[hash]',//映射庫的暴露內容名稱
- path:resolve(__dirname,'dll/manifest.json')
- })
- ],
- mode:'production'
- }
- webpack.config.js
- const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
- module.exports={
- plugins:[
- //告訴webpack哪些庫不參與打包,同時名稱也得變
- new webpack.DllReferencePlugin({
- manifest: resolve(__dirname,'dll/manifest.json')
- }),
- //將某個文件打包輸出出去,并在html中引入該資源
- new AddAssetHtmlWebpackPlugin({
- filepath:resolve(__dirname,'dll/jquery.js')
- })
- ]
- }
編輯推薦