WebpackBundleSplit和CodeSplit之前说到chunk的概念有点模糊,网上的文章大多都是把代码分开动态加载。写这篇文章的目的也是为了让其他和我一样对这个概念不是很清楚的童鞋们有个清晰的认识。事不宜迟,撸起袖子干吧!让我们开始吧!webpack文件分离包括两部分,一是Bundle的分离,二是Code代码的分离:Bundle拆分:其实就是创建多个更小的文件,并行加载,以获得更好的缓存效果;主要功能是启用浏览器并行下载,以提高下载速度。并且使用浏览器缓存,只有当代码被修改,文件名中的哈希值发生变化时,才会重新加载。代码拆分:只加载用户最需要的部分,其余代码遵循懒加载策略;主要作用是加快页面加载速度,不加载不必要的东西。准备工作在文件分离之前,我们先写一些代码:入口文件src/index.js:const{getData}=require('./main')const{findMaxIndex}=require('./math')letarr=[1,2,123,21,3,21,321,1]findMaxIndex(arr)getData('./index.html')两个依赖模块:src/main.js:constaxios=require('axios')constgetData=url=>{axios.get(url).then(d=>{console.log(d.status)console.log(d.data.length)})}module.exports={getData}src/math.js:const_=require('lodash')constfindMaxIndex=arr=>{letx=_.max(arr)letr=Array.prototype.indexOf.call(arr,x)console.log(r);}module.exports={findMaxIndex}添加一个webpack配置文件webpack.config.js:constpath=require('path')module.exports={mode:'development',entry:path.resolve(__dirname,'src/index.js'),output:{path:path.resolve(__dirname,'dist'),filename:'[name].[contenthash].js'},}文件分离前,打包效果在bundlesplit和代码中在拆分操作之前,我们先看一下当前默认打包的效果:所有依赖打包到main.xxx.js中,大小为609kStart分离操作BundleSplitBundleSplit的主要任务是分离多个引用的包和模块,避免所有的依赖被打包到一个文件中。基本使用Webpack4需要使用optimization.splitChunks的配置:constpath=require('path')module.exports={mode:'development',entry:path.resolve(__dirname,'src/index.js'),output:{path:path.resolve(__dirname,'dist'),filename:'[name].[contenthash].js'},optimization:{splitChunks:{chunks:'all'}}}optimization.splitChunks表示从node_modules中打包和分离所有依赖项。我们看看此时打包的文件是什么样子的:增加了splitChunks的配置,我们的第三方模块打包到vendors~main.xxx.js中。文件大小为604k,而入口文件main.xxx.js只有7k。这样,将第三方模块单独打包,可以减少入口文件的体积,但仍然不小;在这个小测试项目中,我们使用了两个第三方模块,axios和lodash,所以我们希望的应该是将这两个模块分成两个文件,而不是全部放在一个vendor里面,然后我们继续配置webpack.config.js:这里把各个npm包分开我们需要用到webpack.HashedModuleIdsPlugin插件参考官方文档直接上传代码:constpath=require('path')constwebpack=require('webpack')module.exports={模式:'开发',入口:path.resolve(__dirname,'src/index.js'),plugins:[newwebpack.HashedModuleIdsPlugin()//根据模块的相对路径生成HASH作为模块ID],output:{path:path.resolve(__dirname,'dist'),filename:'[name].[contenthash].js'},optimization:{runtimeChunk:'single',splitChunks:{chunks:'all',//默认异步可选值all和初始maxInitialRequests:Infinity,//一个入口最大并行请求数minSize:0,//避免模块太小而忽略minChunks:1,//默认也是一个,表示最小引用数cacheGroups:{vendor:{test:/[\\/]node_modules[\\/]/,//如果需要的依赖很小,可以直接设置为需要打包的依赖名称name(module,chunks,chcheGroupKey){//可以提供布尔值、字符串和函数,如果是函数,可以写成自定义返回值constpackageName=module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1]//获取模块名return`npm.${packageName.replace('@','')}`//可选,一般情况下做不需要从模块名称中删除@符号}}}}}}这里我们主要做了几件事情:为了避免每个打包文件的hash变化,我们可以使用webpack内置的HashedModuleIdsPlugin,从而避免每个打包文件的hash值发生变化先增加maxInitialRequests并设置Infinity,指定这个入口文件的最大并行请求数,然后分别设置minSize和minChunks为0和1,即使模块很小也提取出来,这个模块的引用数只有1,并且还提取了最终的配置匹配依赖和分离的文件名格式此外,我们还分离了运行时代码。此代码也可以与InlineManifestWebpackPlugin一起直接插入到HTML文件中。这里我们将这个配置设置为single,即把所有chunk的运行代码打包到一个文件中。这样BundleSplit的操作就基本完成了。看看效果如何:依赖模块分离。使用插件HtmlWebpackPlugin将js代码注入到html文件中。npmi-Dhtml-webpack-plugin修改webpack.config.js文件://配置文件引入这个插件varHtmlWebpackPlugin=require('html-webpack-plugin');//...module.exports={//...plugins:[newHtmlWebpackPlugin(),newwebpack.HashedModuleIdsPlugin()//根据模块的相对路径生成HASH作为模块ID],//...}安装http-server或者使用vscode的插件LiveServer将代码放到本地服务器,打开浏览器的debug窗口进入Network面板:可以看到我们把模块分开,并行加载,比单独加载一个巨大的包要快很多.接下来,我们需要分离代码,延迟加载不需要的模块。拆分代码分离其实就是只加载用户需要使用的部分代码,不需要的暂时不加载。这里我们需要使用require.ensure方法来获取依赖,这样webpack打包后会添加runtime代码,在设置的条件下触发获取依赖。在ES6中,我们可以使用DynamicImports来替代上述方案。如果使用ES6语法,需要使用babel和babel插件plugin-syntax-dynamic-import。为了保证在浏览器中的兼容性,还需要安装promisepolyfill。用法类似,可以直接观察webpack官方文档,修改我们的代码:const{getData}=require('./main')letarr=[1,2,123,21,3,21,321,1]getData('./index.html')setTimeout(()=>{require.ensure(['./math'],function(require){const{findMaxIndex}=require('./math')console.log(findMaxIndex(arr))})},3000)我们设置了一个定时器,只有在3000毫秒后,浏览器才会请求math模块编译完成后,打开调试面板刷新浏览器:页面刚加载完成后,浏览器会try获取上述模块,因为模块很小,加载完成很快。大约在3500ms左右,浏览器会获取到requie.ensure方法定义的math模块。因为math模块也依赖lodash,所以这个lodash第三方模块也是按需加载的。这样,我们就完成了代码分离的操作。这样做的好处是可以将不需要在第一时间加载的模块延迟加载,以页面加载速度加载。当然,上面的超时定时器完全可以换成按钮点击等其他事件来触发。结尾
