Webpack4 学习(四)性能优化 作者:pandali 时间:2021年09月19日 分类:计算机技术,JS,Html 字数:6004 warning: 这篇文章距离上次修改已过277天,其中的内容可能已经有所变动。 ### webpack性能优化 - 开发环境优化 - 生产环境优化 ### 开发环境优化 - 优化打包构建速度 - 优化代码调试 ### 生产环境优化 - 优化打包构建速度 - 优化代码运行的性能 ### HMR(比较鸡肋) hot module replacement 热模块替换 一个模块发生变化,只编译这个模块,其他模块不动 - 样式文件:可以使用,因为style-loader内部实现了 - JS文件:默认没有不能使用解决办法: ``` 在JS文件中编写 if(module.hot) { module.hot.accept('./print.js',function(){ print(); }) } ``` 只能修改非入口文件进行修改 - html文件:默认没有,不能使用,且html不自动热更新了。热更新解决办法:修改entry入口,将html文件引入 ``` ectry:["./src/index.js",'./src/index.html'], ``` ``` devServer:{ contentBase:resolve(__dirname,'dist'), compress:true, port:3000, open:true, // 开启HMR功能 hot:true } ``` ### source-map 一种 "提供源代码到构建后代码映射" 技术 如果构建后代码出错了,可以定位到源代码出错位置 ``` webpack中配置 devtool:"source-map" ``` 配置选项有:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map - source-map:外部 错误代码准确信息 和 源代码错误位置 - inline-source-map:内联 速度快 只生成一个source-map 错误代码准确信息 和 源代码错误位置 - hidden-source-map:外联 生成外部文件 错误代码准确信息 但 没有源代码错误位置 只有构建后的错误位置 - eval-source-map:内联 每一个文件都生成对应的source-map,都在eval 错误代码准确信息 和 源代码错误位置 - nosources-source-map:外部 错误代码准确信息 但是没有任何源代码信息 - cheap-source-map:外部 错误代码准确信息 和 源代码错误位置 错误代码精确到行 - cheap-module-source-map:外部 错误代码准确信息 和 源代码错误位置 会将loader的source-map加入进来 ##### 速度排序 eval > inline > cheap ##### 开发环境 - 速度快: eval-cheap-source-map eval-source-map - 调试友好: souce-map cheap-module-souce-map cheap-souce-map 综合考虑: eval-source-map / eval-source-module-source-map ##### 生产环境 hidden-source-map nosources-source-map ### onOf - 使用oneOf 根据文件类型加载对应的loader,只要能匹配一个即可退出, - 对于同一类型文件,比如处理js,如果需要多个loader,可以单独抽离js处理,确保oneOf里面一个文件类型对应一个loader - 可以配置 enforce: 'pre',指定优先执行  ### 缓存 ##### babel缓存 ``` { test:/\.(js)$/, exclude:/node_modules/, loader:"babel-loader", options:{ presets:[ "@babel/preset-env" // 预设:指示babel做什么样的兼容性处理 ], // 开启babel缓存,第二次构建时会读取之前的缓存 cacheDirectory:true } } ``` ##### 文件缓存 让代码上线运行缓存更好使用 - hash:每次打包都生成一个唯一的hash值 问题:js和css同时使用一个hash值,导致所有缓存失效 - chunkhash:根据chunk生成hash,如果打包来自一个chunk,那么hash值一样 当css时js中引入的,所以同属于一个chunk值 - contenthash:根据文件内容生成hash ### tree shaking 去除无用 js 代码,减少代码包体 在package.json 中配置 "sideEffects":false,所有代码都没有副作用,都可以进行tree shaking可能会把css/@babek/polyfill 干掉 "siteEffects":["*.css"] #####JS前提: - 必须使用es6模块化 - 开启production ### code split JS 代码切割工具 ##### 按文件名切割 ``` entry:{ main:"./src/main.js", test:"./src/test.js" }, output:{ filename:'js/[name].[contenthash:10].js', path:resolve(__dirname,'build) } ``` ##### 按照chuck切割 将nodel_modules 中代码单独打包一个chunk最终输出 ``` plugins:[], optimization:{ splitChunks:{ chunks:'all' } } ``` ##### 文件+chunk 自动分析,多入口chunk中,有没有公共的文件,如果有回到包成一个chunk ``` entry:{ main:"./src/main.js", test:"./src/test.js" }, output:{ filename:'js/[name].[contenthash:10].js', path:resolve(__dirname,'build) }, plugins:[], optimization:{ splitChunks:{ chunks:'all' } } ``` ##### 通过js代码,让某个文件被打包成一个chunk 当在JS中通过引入的方式,使用单入口时会导致引入的JS打包到当前的JS中 解决办法如下:动态导入方法 ``` import('./test') .then((result)=>{ console.log(result) }) .catch(()=>{ console.log(error) }) ``` ### JS文件懒加载 - 懒加载:当文件需要使用时才加载 - 预加载prefetch:会在使用前提前加载js文件(等其他资源加载完毕,浏览器空闲了,再偷偷加载资源) 不适用于 手机网页开发 ``` #懒加载 import(/*webpackChunkName:'test'*/'./test') .then(({mul})=>{ console.log(mul(2,5)) }); #预加载 import(/*webpackChunkName:'test',webpackPrefetch:true*/'./test') .then((mul)=>{ console.log(mul(3,5)); }) ``` ### PWA 渐进式网络开发应用程序(离线可访问) 代码必须运行在web服务器上 使用work-webpack-plugin插件 npm i work-webpack-plugin ``` new WorkboxWebpackPlugin.GenerateSW({ /* ** 1.帮助serviceworker快速启动 ** 2. 删除旧的serviceworker ** 生成一个serviceworker文件 */ clientClaim:true, skipWaiting:true }) ``` 在入口文件中配置servicework service work 有兼容性问题,需要哦按段 ``` if('serviceWork' in navigator) { navigator.serviceWork .regsiter('/service-worker.js') .then(()=>{ console.log('sw 注册成功') }) .catch(()=>{ console.log('sw 注册失败'); }); } ``` ### 多进程打包 进程启动大概需要600ms,小项目不建议使用,因为是最后一步,所以需要放到最上面 ``` use:[ "thread-loader" ] use:[ { loader:"thread-loader", options:{ workers:2 //2 个进程 } } ] ``` ### externals 拒绝某些包被打包进来,使用script直接src引用 ``` mode:"production", externals:{// jquery:"jQuery" } ``` ### dll 使用dll技术,对某些库(第三方库:jquery,ract,vue..)进行单独打包 ##### 创建webpack.dll.js ``` /* 使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包 当你运行 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: 'production' }; ``` ##### webpack.config.js ``` const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~ new webpack.DllReferencePlugin({ manifest: resolve(__dirname, 'dll/manifest.json') }), // 将某个文件打包输出去,并在html中自动引入该资源 new AddAssetHtmlWebpackPlugin({ filepath: resolve(__dirname, 'dll/jquery.js') }) ], mode: 'production' }; ```