【javascript】webpack code split && tree shaking

webpack打包vue项目之后的文件太大,本身我们项目的体量也比较大,首次加载太慢。

所以尝试代码分割,对打包之后的app.js进行拆分。

1,动态加载 -(路由懒加载) -(按需加载)

    现在vue项目里面,有很多路由,一个路由对应着或者多个路由对应着一个组件,如果不进行代码拆分,所有的这些组件在打包的时候都会被打包在app.[hash].js里面,但是很多组件在首次访问的时候是不需要的。

    我们可以将不同路由对应的组件,打包成不同的文件,在访问某一个特定路由的时候,才去加载这个路由所对应组件的js。访问项目首页的时候,我们只需要加载index page所需的组件即可。

    比如我们之前的路由加载首页组件是这样的:        

import Vue from 'vue'
import Router from 'vue-router'
import PlainComponent from '@/components/PlainComponent'
import Index from '@/components/Index'
Vue. use( Router)

export default new Router({
routes: [
{
path: '/',
name: 'index',
component: Index
},
{
path: '/plain-component',
name: 'PlainComponent',
component: PlainComponent
}
]
})

    然后我们加入其他的路由,在routes节点增加新的item,比如有新的需求,组件名为PlainComponent,是访问路由/plain去访问然后加载的。在webpack.prod.config.js里面,将 UglifyJsPlugin暂时先注释,关闭sourceMap,跑npm run build后,查看打包文件,你会发现 PlainComponent的内容被打包到了app.js里面。这不是我们需要的,我们首页访问的时候 不需要加载这个组件。

    这时候我们可以将这个组件设置为动态导入

    我们动态引导入此组件,webpack会自动将该组件打包成一个chunk文件,可以手动命名,命名方式长这样:

扫描二维码关注公众号,回复: 4728840 查看本文章

/*webpackChunkName: "your-chunk-name"*/

component : () => import( /* webpackChunkName: "DynamicComponent" */ '../components/DynamicComponent')

   

   为了方便对比,我把普通组件,和动态导入组件的路由都留到了项目的路由文件中, 现在路由文件长这样:

export default new Router({
routes: [
{
path: '/',
name: 'index',
component: Index
},
{
path: '/plain-component',
name: 'PlainComponent',
component: PlainComponent
},
{
path: '/dynamic-component',
name: 'DynamicComponent',
component : () => import( /* webpackChunkName: "DynamicComponent" */ '../components/DynamicComponent')
}
]
})

   tip-1:  到这一步,我们已经拆分了app.js,但是你可能会有疑问,如果路由里面多个路由对应一个组件,比如/dynamic-component-2 也会对应使用组件DynamicComponent组件【实际业务也会遇到】,这时候我们仍然可以使用动态导入,无论是否使用同样的webpackChunkName。这个组件只会被打包一次,最后只会生成一个chunk文件,如果name不一样以第一次命名为主。

   tip-2:  随着动态导入的组件越多,各个组件里面的引入的其他模块可能会被重复打包,比如说上面除了第一个动态导入的DynamicComponent组件,还有另外的动态导入的组件DynamicComponentTwo 和 DynamicComponentThree,都使用到了lodash的omit方法。打包之后,查看对应的三个js文件,你会发现omit被导入了三次,这样也就重复了三次。所以我们需要将这三个chunk的公用部分给提取出来,这时候需要用到 CommonsChunkPlugin。【代码请查看后面的demo连接】

    现在我们的路由文件长这样:

export default new Router({
routes: [
{
path: '/',
name: 'index',
component: Index
// component: require('../components/Index').default
},
{
path: '/plain-component',
name: 'PlainComponent',
component: PlainComponent
},
{
path: '/dynamic-component',
name: 'DynamicComponent',
component : () => import(
/* webpackChunkName: "DynamicComponent" */
'../components/DynamicComponent')
},
{
path: '/dynamic-component-two',
name: 'DynamicComponentTwo',
component : () => import(
/* webpackChunkName: "DynamicComponentTwo" */
'../components/DynamicComponentTwo'
)
},
{
path: '/dynamic-component-three',
name: 'DynamicComponentThree',
component : () => import(
/* webpackChunkName: "DynamicComponentThree" */
'../components/DynamicComponentThree'
)
}
]
})


2, CommonsChunkPlugin 

    commonsChunksPlugin可以帮助我们,抽取多个chunk的公共部分,正如上面我们的例子,三个动态导入的chunk,都用到了lodash的omit方法,我们需要抽取出来。

    这时候我们在webpack.prod.config.js文件里面plugins添加一个item:

new webpack. optimize. CommonsChunkPlugin({
name: 'app',
async: "vendor-async",
minChunks: 3
})

我们先明白这个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:

    1, webpack-code-splitting

    2,   tree shaking


第二个例子是基于第一个来做的,你可以查看打包之后的vendor-async.xxx.js的大小对比,然后各自启一个web server 查看在加载动态组件的时候,console控制台里面关于 _ 的差异

        

    

猜你喜欢

转载自blog.csdn.net/a5534789/article/details/79955426