当前位置: 首页 > Web前端 > HTML5

webpack性能调优报告,极限压缩monaco编辑

时间:2023-04-05 17:40:03 HTML5

调优结果优化前和优化后比例打包体积32.01MB13.57MB57.61%Gzip体积3.37MB1.47MB??文件数量821976.83%MB56.38%资源压缩ServerbrCompressionClientbrCompression1压缩级别611(最大)1构建速度60s+41.1s+31.67%播放页面FP耗时明显感知无感知1(以上数据来源为本地测试,仅供参考)优化目标减少播放页面FP耗时减少静态资源请求次数提升开发体验提升构建产品质量(文章基于webpack@4.46.0)问题分析先看当前打包分析结果project以上是vue-cli生成的构建统计报告,有助于分析包中包含的模块大小,简单罗列报告信息如下:整体包体积为32.01MBts.worker.js是monaco编辑器语言支持文件,主要提供typescript语法支持,体积10.92MBmy-details.js为播放页面文件,网站核心资源文件,体积6.81MBchunk-vendors.js第三方模块包,体积4.14MBhtml.workar.js是monaco编辑器的html语法支持文件css.workar.js是monaco编辑器的css语法支持文件json.worker.js文件是monaco编辑器的json语法支持文件app.js项目入口文件编辑器,体积为1.02MB。小文件太多,加起来近百个,导致http请求过多。经过分析总结,定位问题如下:monaco-editor是最大的问题。其体积占据半壁江山,严重影响加载速度。需要将chunk-vendors.js作为一个公共模块进行优化,构成了一些项目不可或缺的基础类库。都是需要的,现在太大了,应该在合理范围内拆分成更小的js,这样才能利用浏览器的并发请求,优化首页加载体验。它包含三个大家伙:elementUI、moment和lodash,需要单独优化。my-details.js和app.js是项目的主要资源文件。体积太大,需要优化的小文件太多。他们需要合并解决思路是检查是否有不必要的输出。在有限的时间空间和计算能力下,去除低效的重复(提出公共大模块),合理冗余(小文件允许重复),达到时间和空间综合考虑的最佳。下面一步步实现每个优化项。monaco-editor优化和缩减了我们的业务级别的规模。我们只需要用到显示功能,它的语言编辑功能根本不用。因此,我们可以过滤掉所有的语言包。这里我们需要使用monaco-editor-webpack-plugin插件,配置并添加插件选项languages是支持的语言数组(具体语言见官网)。默认支持所有语言。配置此选项应该只会删除对某些语言的高级功能支持。newMonacoWebpackPlugin({languages:[],}),再次打包后,体积缩小到9.26MB。将monaco-editor单独打包成一个重量级的组件,会把很多小文件分散到各个地方,从而增加文件的数量和体积,造成流量损失。通过webpack的splitChunks功能将monaco-editor拆分成单独的独立文件,充分利用浏览器缓存,减少多次引用的消耗。splitChunks:{chunks:'all',minSize:20000,maxAsyncRequests:30,maxInitialRequests:30,enforceSizeThreshold:50000,maxSize:0,cacheGroups:{monacoEditor:{chunks:'async',name:'chunk-edit-mona',priority:22,test:/[\/]node_modules[\/]monaco-editor[\/]/,enforce:true,reuseExistingChunk:true,},}按需加载通过按需加载,减少核心渲染前的内容阻塞,需要的时候去加载(目前只需要editor.api和jscss高亮功能)。asyncinitEditor(){constmonaco=awaitimport(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/editor/editor.api.js');//引入高亮模块awaitthis.highlightLang()}//引入语言高级模块highlightLang(){constLANGUAGES=['javascript','css','html','mysql','java','python','markdown','go','lua'];constpromiseList=[import(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'),import(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/basic-languages/css/css.contribution'),//import(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/basic-languages/html/html.contribution'),//import(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/basic-languages/mysql/mysql.contribution'),//import(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/basic-languages/java/java.contribution'),//import(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/basic-languages/python/python.contribution'),//import(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution'),//导入(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/basic-languages/go/go.contribution'),//import(/*webpackPrefetch:true;"*/'monaco-editor/esm/vs/basic-languages/lua/lua.contribution'),];returnPromise.all(promiseList);}开启prefetch按需加载后,由于monaco-editor本身很大,在加载编辑器的时候会出现If长时间无响应,使用prefetch预加载方案,在浏览器空闲时预下载资源,使用时直接获取,有效避免无响应constmonaco=awaitimport(/*webpackPrefetch:true;"*/'monaco-editor');moment.js优化尝试删除moment.js语言包后,体积还是很大。最后换成dayjs,完全兼容moment.jsapi。gzip压缩后只有2kb(因为替换工作是全局的,可能会出现moment.js功能相关的Bugs)element-ui进行了优化,单独打包理论上UI组件库也可以放到chunk-vendors.js,但确实太大了,可能比libs中所有包加起来还要大不少,而且UI组件库的更新频率相对chunk-vendors.js要高一些。Element-UI组件库作为UI组件,应该从chunk-vendors.js中分离出来,单独打包为chunk-elementUI.js,如图。使用的组件,体积减少。lodash.js优化按需介绍使用webpack插件lodash-webpack-plugin和babel插件实现按需打包lodashconstLodashModuleReplacementPlugin=require('lodash-webpack-plugin');config.plugin('loadshReplace').use(newLodashModuleReplacementPlugin());module.exports={presets:['@vue/cli-plugin-babel/preset'],plugins:['lodash',],};优化后lodash基本感知不到svgIcon的存在,单独优化将svgIcon组件库封装为更新频繁,引用量大的库。应该分离成chunk-svgIcon.js,如图。-zh.svg现在项目中没有用到,所以删除sprite图片,将所有svg合成为sprite图片,减少请求次数,使用svg-sprite-loader实现constsvgRule=config.module.rule('svg');//清除所有现有的加载器,如果你不这样做,下一个加载器将被追加到这个规则的现有加载器之后。svgRule.uses.clear();//添加加载器来替换vgRule.test(/.svg$/).include.add(path.resolve(__dirname,'src/components/svgIcon/svg')).end().use('svg-sprite-loader').loader('svg-sprite-loader').options({symbolId:'icon-[name]',}).end();compressedsvg(不执行)因为压缩后的svg图片会去掉fill,所以这一步不进行代码压缩使用terser插件(webpack4官方推荐)压缩js和css代码,去掉生产环境注释和控制台信息config.optimization.minimizer('terser').tap(options=>{constcompress={warnings:false,drop_console:true,drop_debugger:true,pure_funcs:['console.log'],};constinitCompress=options[0].terserOptions.compress;options[0].terserOptions.compress={...initCompress,...compress};返回选项;});图片压缩(未实现)使用webpack插件image-webpack-loader对所有图片进行压缩,但该插件依赖系统环境,不同环境下可能会出现安装失败和编译失败。我们的项目目前图片资源很少,所以暂时不用。启用br压缩目前br的压缩是在nginx服务器上进行的,不缓存资源,所以每次请求发送出去之前都需要对资源进行压缩。考虑到服务端性能,压缩级别设置为6级,现在改为客户端压缩,服务端收到请求时直接发送压缩文件,减轻服务端压力。同时客户端压缩可以将压缩级别调到最高11,整体资源大小会再次下降,使用compression-webpack-plugin来实现。constCompressionWebpackPlugin=require('compression-webpack-plugin');if(!isDev){plugins.push(newCompressionWebpackPlugin({文件名:'[path].br[query]',算法:'brotliCompress',测试:/.(js|css|json|txt|html|ico|svg)(?.*)?$/i,compressionOptions:{params:{[zlib.constants.BROTLI_PARAM_QUALITY]:11,},},threshold:1024,minRatio:0.99,//删除原文件,只保留压缩文件deleteOriginalAssets:false,}),);}多线程执行加载器启用并行Babel或TypeScript使用thread-loaderparallel:require('os').cpus().length>1打包缓存使用HardSourceWebpackPlugin插件为模块提供中间缓存。缓存的默认存放路径为:node_modules/.cache/hard-source。配置hard-source-webpack-plugin,第一次构建时间变化不大,但是第二次启动,构建时间可以节省80%左右。可能的问题,修复在这个hard-source-webpack-plugin下面的链接是用来解决hashloss问题的https://github.com/mzgoddard/...其他修改项提取环境变量const`isDev=过程。env.NODE_ENV==='开发';`增加包装速度检测插件SpeedMeasurePlugin最终报告报告图: