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

webpack高级配置

时间:2023-03-28 12:16:08 HTML

treeshaking(树摇动)主要想说说treeshaking失败的原因(treeshaking失败的原因)。首先说一下treeshaking本身的效果。什么是摇树?比如先配置webpack.config.jsconstwebpack=require("webpack");/***@type{webpack.Configuration}*/module.exports={mode:"production"};在固定的a.js使用esm导出,b.js使用commonjs导出不变//a.jsexportfunctionf1(){console.log("11111");}exportfunctionf2(){console.log("22222");}//b.jsexports.f3=function(){console.log("33333");};exports.f4=function(){console.log("44444");};示例1:importa.jsandrequireb.js//index.jsimport{f1}from"./a";import{f3}from"./b";console.log(f1);console.log(f3);打包结果:a.j和b.js都是treeshaking,只输出f1和f3。所以可以用import导入,esm和commonjs都可以导出。例子2:importa.jsandimportb.js//index.jsimport{f1}from"./a";const{f3}=require("./b");console.log(f1);console.log(f3);打包结果:a.js被抖动,b.js不被抖动,f1,f3,f4是输出。所以使用require导入是不成功的。结论:treeshaking只能导入,esm和commonjs都可以导出。因为treeshaking发生在编译阶段,所以只支持esmimport,不支持commonjsrequire,因为编译esm,运行commonjs。tree-shaking失败的可能原因有以下三种:1.代码没有使用import导入2.webpack配置没有开启tree-shaking3.副作用(sideEffects)4.babel配置preset-env没有开启writemodule:false,参数代码不使用import关于这一点的介绍上面已经说明,必须使用import导入,导出用esm或者commonjs。Webpack配置不启用treeshaking。启用摇树。两步:1.UsedExports设置为true,标记无用代码,将esm导出的未使用导出函数标记为未使用和声导出f2,将commonjs导出的未使用导出函数赋值为__webpack_unused_export__2,terser-webpack-plugin插件进行代码压缩去除无用代码,根据两个标记一步到位,压缩后的代码会去除constwebpack=require("webpack");/***@type{webpack.Configuration}*/module.exports={mode:"none",optimization:{usedExports:true}};mode:在production模式下,默认开启treeshaking,不需要做任何配置,从源码可以看出none和development不会启用treeshaking,需要手动添加这两步,注意设置minimize:true,或者放在plugins中查看webpack源码的默认配置,详见前端进阶面试题解答副作用(sideEffects)先解释一下什么是副作用:在当前作用域之外修改行为称为副作用,比如在函数内部修改dom,修改全局对象等。sideEffects字段默认为true以指示副作用。它可以设置为false以指示没有副作用。设置为数组以列出具有副作用的文件。在webpack.config.js中设置sideEffects:true表示检查在三方包的sideEffects字段中,webpack在使用userExports标记无用代码时,如果无法判断库中的代码是否有副作用,则不会标记,压缩时也无法清除。如果它判断有副作用,则不会标记它。clear模式:生产模式下默认开启treeshaking,无需任何配置,usedExports:trueconstwebpack=require("webpack");constTerserPlugin=require("terser-webpack-plugin");/***@type{webpack.Configuration}*/module.exports={mode:"none",optimization:{sideEffects:true,usedExports:true},plugins:[newTerserPlugin()]};babel配置preset-env没有写module:false参数在我掌握的babel配置一文中,对module:false参数进行了详细的解释。简单的说,当不设置false时,require只会用于引入babel相关的运行时包,设置false时会使用import进行导入。树,回到第一点module.exports={presets:[["@babel/preset-env",{modules:false},],]};拆包(splitChunks)splitChunks是在webpack配置下的optimization配置,也就是optimization看单词理解的意思就是拆分多个chunks。什么是chunkwebpack,本质是将多个js模块合并为一个js,即一个入口得到一个输出js文件(bundle.js)。但是问题是如果bundle.js文件很大,浏览器请求的时候,请求时间会很长,首屏会空白很长时间。所以优化的方法是将bundle.js文件拆分成多个小的js文件同时请求。当然,首屏的渲染和显示会更快。因此,入口文件、chunk文件、输出文件的关系由原来的一个入口文件对应一个chunk,最后输出一个bundle文件,变成了一个入口文件对应多个chunk,最后输出多个bundle文件,三个bundle文件chunk1的获取方式和入口文件Chunk可以生成,入口文件是webpack配置的入口选项;2、异步请求导入函数调用或者require.ensure可以生成chunk;例如:import函数就是我们写vue-router时写的异步请求路由方法,这里的webpackChunkName可以神奇的定义chunk名称,或者不写import(/*webpackChunkName:"AboutPage"*/'./view/about.vue')3.webpack配置splitChunks手动拆分生成chunks,最后独立输出到js文件splitChunks配置简单配置,将react相关包单独提一个文件{optimization:{splitChunks:{chunks:"all",//initial,asyncandallcacheGroups:{react:{name:"react",test:/[\\/]react(\w)*[\\/]/i,priority:10},lodash:{name:"lodash",test:/[\\/]lodash(\w)*[\\/]/i,priority:20,minChunks:3},},},},}我们来看看webpack默认的splitChunks参数。看图片。生产模式和非生产模式有不同的参数。以下参数表示自动解包的条件:chunks重要:解包的范围,默认async,只针对异步请求,即上面第二项中import函数调用的chunk;initial表示仅用于初始化入口en尝试;all表示最大包含async+entrycacheGroups重要:自定义解包规则,name为chunk名称,测试正则包名,priority优先(因为同一个包可能满足多个解包规则,会按照优先级高的处理)从图中可以看出,默认有两条包规则,defaultVendors规则是指node_modules会被拆分成一个chunk包,default规则是指只有被两个或多个chunk引用,才会拆分成一个chunkpackage.minChunks拆分前必须共享模块的最小chunk数不需要修改maxAsyncRequests浏览器发送异步请求时,最大请求数不超过30,即在import函数中调用上面第二项不需要修改maxInitialRequests,浏览器请求入口时,最大不超过30个,热更新不需要修改。我们m主要解释一下热更新的module.hot.accept()。我们先了解一下热更新是怎么配置的?热更新配置包npmi-Dwebpack-dev-serverhtml-webpack-pluginwebpack.config.jsconstwebpack=require("webpack");constHtmlWebpackPlugin=require('html-webpack-plugin');/***@type{webpack.Configuration}*/module.exports={mode:"development",devServer:{port:3000,open:true,hot:true,},plugins:[newHtmlWebpackPlugin(),]};package.json"scripts":{"serve":"webpackserve",},结论是热更新配置完成,代码写的也正常,但是发现了一个问题。此时更新页面是整个刷新页面,而不是局部刷新。如何?怎么回事,原来需要在每个文件末尾加上module.hot.accept()来触发局部更新,accept是可以接受的两个参数,依赖和回调{module.hot.accept();}马上又出现一个问题,这个太麻烦了,每个文件都要加上module.hot.accept(),但是我们真正写下工程的时候怎么办呢?这句话没写?原因是不管css、vue、react的loader,都会自动帮我们加上这句话。CSS有style-loader,react有react-hot-loader,vue有vue-loader。对于jsx文件,有vue-jsx-hot-loader{test:/\.jsx?$/,use:['babel-loader','vue-jsx-hot-loader']}可以按需加载一些有一段时间,我一直在混淆treeshaking和按需加载。其实应该分开理解。这里主要想说一下第三方包的按需加载。比如使用element-ui、lodash、vanttreeshaking的前提是使用import来导入,但是按需加载是不需要的,还有一点需要注意:如果是我们封装的库,比如a元件库,导出格式根据文件类型不同。如果是js文件,可以是commonjs+es5,esm+es5;如果是vue或者react文件,esm/commonjs+es6/es5就可以了,因为我们在使用babel-loader的时候,会将node_modules目录排除在编译之外,vue-loader会编译vue文件。使用babel插件npminstallbabel-plugin-component-Dbabel.config.jsmodule.exports={presets:[["@babel/preset-env",{useBuiltIns:"usage",corejs:2,modules:false,},],],插件:[["@babel/plugin-transform-runtime"],["babel-plugin-import",{libraryName:"vant",libraryDirectory:"es",style:true,},"vant",],["babel-plugin-import",{libraryName:"antd",style:true,//或'css'},],["babel-plugin-import",{"libraryName":"lodash","libraryDirectory":"","camel2DashComponentName":false,//默认:true},"lodash",],["babel-plugin-component",{libraryName:"element-ui",styleLibraryName:"theme-chalk",},"element-ui",],],};完成!