webpack打包vue项目之后的文件太大,本身我们项目的体量也比较大,首次加载太慢。
所以尝试代码分割,对打包之后的app.js进行拆分。
现在vue项目里面,有很多路由,一个路由对应着或者多个路由对应着一个组件,如果不进行代码拆分,所有的这些组件在打包的时候都会被打包在app.[hash].js里面,但是很多组件在首次访问的时候是不需要的。
我们可以将不同路由对应的组件,打包成不同的文件,在访问某一个特定路由的时候,才去加载这个路由所对应组件的js。访问项目首页的时候,我们只需要加载index page所需的组件即可。
比如我们之前的路由加载首页组件是这样的:
然后我们加入其他的路由,在routes节点增加新的item,比如有新的需求,组件名为PlainComponent,是访问路由/plain去访问然后加载的。在webpack.prod.config.js里面,将 UglifyJsPlugin暂时先注释,关闭sourceMap,跑npm run build后,查看打包文件,你会发现 PlainComponent的内容被打包到了app.js里面。这不是我们需要的,我们首页访问的时候 不需要加载这个组件。
这时候我们可以将这个组件设置为动态导入:
我们动态引导入此组件,webpack会自动将该组件打包成一个chunk文件,可以手动命名,命名方式长这样:
/*webpackChunkName: "your-chunk-name"*/
为了方便对比,我把普通组件,和动态导入组件的路由都留到了项目的路由文件中, 现在路由文件长这样:
tip-1: 到这一步,我们已经拆分了app.js,但是你可能会有疑问,如果路由里面多个路由对应一个组件,比如/dynamic-component-2 也会对应使用组件DynamicComponent组件【实际业务也会遇到】,这时候我们仍然可以使用动态导入,无论是否使用同样的webpackChunkName。这个组件只会被打包一次,最后只会生成一个chunk文件,如果name不一样以第一次命名为主。
tip-2: 随着动态导入的组件越多,各个组件里面的引入的其他模块可能会被重复打包,比如说上面除了第一个动态导入的DynamicComponent组件,还有另外的动态导入的组件DynamicComponentTwo 和 DynamicComponentThree,都使用到了lodash的omit方法。打包之后,查看对应的三个js文件,你会发现omit被导入了三次,这样也就重复了三次。所以我们需要将这三个chunk的公用部分给提取出来,这时候需要用到 CommonsChunkPlugin。【代码请查看后面的demo连接】
现在我们的路由文件长这样:
2, CommonsChunkPlugin
commonsChunksPlugin可以帮助我们,抽取多个chunk的公共部分,正如上面我们的例子,三个动态导入的chunk,都用到了lodash的omit方法,我们需要抽取出来。
这时候我们在webpack.prod.config.js文件里面plugins添加一个item:
我们先明白这个CommonsChunkPlugin的参数是什么意思。
name首先分为两种,第一种是属性值是已知的chunk块的名称,这里是app,说明我们要对app的子模块进行公共代码的抽取。【如果使用了新的name,则表示生成新的chunk文件,在本例的源代码中尝试会有问题,因为三个子模块都是动态导入的,若是三个非动态引入name + chunks(所有非动态引入组件)+ minChunks是可以执行的 】
async是抽取异步chunk的意思,【和filename是冲突的,设置filename则会生成以filename属性命名的chunk,但是是同步加载,当加载首页的时候,会同时加载以filename命名的js文件】,异步的意思是加载首页,我们不需要加载这一块公共代码,只有某些特定的模块使用了这个chunk的数据时,加载这个特定的模块才会去加载这个异步chunk。
minChunks这里设置为3,表示至少3个模块以上出现的同样的代码,才会将代码提取出来生成common chunk。
我们没有抽取异步公共组件之前,三个动态导入的组件打包后是这样的:
然后我们跑构建命令,然后查看会多出来一个 vendor-async.xxx.js的文件,这个文件里面就是三个动态引入的组件公共的代码,也就是lodash部分,然后再看三个文件的大小,由70多k变成了1k以下:
3, tree shaking
到这一步就完成了吗?完成了大部分的压缩拆分文件的效果了,但是打开vendor-async.xxx.js文件,你会发现,lodash的所有方法都被加载进了这个文件 ,并且在访问动态路由的时候,我们直接在chrome 的console里面直接输入_.isNil,或者其他lodash方法都会被打印出来,uglifyJS没有移除那些没有使用的方法,明明我们项目使用的只有一个omit方法,也就是说理想的情况是vendor-async 这个chunk不应该那么大,所以我们应该考虑tree shaking 【移除没有使用的代码】。 因为lodash没有使用ES6的模块化语法,tree shaking的前提是webpack处理的代码,如果采用了ES6模块化语法,uglifyJS会自动帮我们去除的。
所以去掉lodash中没有的代码,需要用到额外的插件:babel-plugin-lodash
加入之后看vendor-async.xxx.js的大小
--------------------
上文提到的两个例子demo:
2, tree shaking
第二个例子是基于第一个来做的,你可以查看打包之后的vendor-async.xxx.js的大小对比,然后各自启一个web server 查看在加载动态组件的时候,console控制台里面关于 _ 的差异