开发环境性能优化
优化打包构建速度
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.
编辑推荐