webpack 性能优化

今天我为大家介绍一下webpack 性能优化
首页 新闻资讯 行业资讯 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();     }) }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

html文件: 默认不能使用HMR功能,同时会导致问题:html文件不能热更新了 解决:改 entry:['入口js','html'] ,但html文件只有一个,所以不用做HMR功能

复制

devServer:{         //项目构建后的目录         contentBase: resolve(__dirname,'build'),         //启用gzip压缩         compress:true,         //端口号         port:3000,         //自动打开浏览器         open:true     }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

优化 开发环境 代码调试
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.内联构建速度更快
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

开发环境:速度快,调试更友好

复制

速度快 eval>inline>cheap>... 调试更友好 souce-map>cheap-module-souce-map>cheap-souce-map 所以 一般用eval-source-map
  • 1.

  • 2.

  • 3.

生产环境:源代码要不要隐藏,调试要不要友好?内联会让体积编码,所以一般不用内联

 

复制

nosources-source-map 隐藏源代码 hidden-source-map 只隐藏源代码,会提示构建购代码错误信息 --> source-map /cheap-module-souce-map
  • 1.

  • 2.

  • 3.

优化生产环境
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.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

  • 79.

  • 80.

  • 81.

  • 82.

  • 83.

  • 84.

  • 85.

  • 86.

  • 87.

缓存
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         }     },
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

2.文件资源缓存-->上线缓存优化

复制

hash:每次webpack构建会生成一个唯一hash值         问题: 因为js和css同时使用一个hash值,如果重新打包,会导致所有缓存失效,可能我却只改了一个文件,     chunkhash:根据chunk生成hash值,如果打包来源于同一个chunk,那么hash值也一样         问题:js和css的hash值还是一样的。             原因:css是由js引入的,所以属于同一个chunk     contenthash: 根据文件内容生成hash值,
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

复制

webpack.config.js
  • 1.

tree shaking
去除应用程序中没有使用的代码

复制

前提: 1.必须使用es6模块化 2.开启production模式  在package.json中配置 "sideEffects":false 所有代码都没有副作用,都可以镜像tree sharking      问题 可能会把css/@babel/polyfille 干掉 "sideEffects": ["*.css","*.less"] 哪些文件不 tree sharking
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

code split
1.入口配置

复制

单入口 //单页面应用     entry:'./src/js/index.js'      多入口 //多页面应用     entry:{         index:'./src/js/index.js',         test:'./src/js/test.js'     }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

2.optimization

复制

module.exports={     //...     // 可以将nodemudules中的代码单独打包成一个chunk最终输出     // 还可以自动分析多入口chunk中,有没有公共的文件,如果有会打包成一个单独的chunk     optimization:{         splitChunks:{             chunks:'all'         }     } }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

3.import 动态导入语法,能将某个文件单独打包
通过js代码,让某个文件被单独打包成一个chunk,通过注释可以固定此文件的名称

复制

import(/*webpackChunkName: 'xxxName' */'./xx/xxx.js')     .then(res =>{         //加载成功     })     .catch(()=>{         //加载失败     })
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

懒加载和预加载
1.懒加载 当文件需要用时才加载
import 动态导入语法

复制

document.getElementById('btn').onclick = function(){     import(/*webpackChunkName: 'xxxName' */'./xx/ss.js')     .then(res=>{         //干啥干啥     }) }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

2.预加载 webpackPrefetch:true
./xx/ss.js 已经被加载了,点击的时候再从缓存中加载,

复制

document.getElementById('btn').onclick = function(){     import(/*webpackChunkName: 'xxxName',webpackPrefetch:true */'./xx/ss.js')     .then(res=>{         //干啥干啥     }) }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

正常加载可以认为是并行加载(同一时间加载多个文件) 预加载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下的资源作为静态资源暴露出去
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

多进程打包
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                 }         }     ]     },
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

externals

复制

module.exports={     externals:{         //忽略/拒绝  库名 -- npm 包名          //可以在index.html中引入cdn     } }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

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')         })     ] }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

编辑推荐

  1. 为何微服务是业务成功的关键?

  2. 缓存,确实很香,却也很受伤!

  3. 学会这5招,让Linux排障更简单

  4. 十张图说清Elasticsearch原理!

  5. Windows Server与Linux:究极对比

 

13    2020-09-19 21:26:56    webpack