对于大型的webpack而言,如果把所有的东西都放在一个文件中打成一个包,这是十分低效的,也是不可接受的。webpack在默认情况下会把所有的文件都打包成一个文件(也就是一个包),代码拆分要做的就是把单个bundle文件拆分成若干个小bundles/chunks,这样可以缩短首屏的加载时间。
- 手动定义多个入口
- splitChunks提取公有代码,拆分业务代码与第三方库
将公有代码,业务代码和第三方库进行拆分是一个更有效的方式,业务代码随着需求的改动会有改动,但是第三方库我们几乎是不改变的,这样进行拆分后的第三方库缓存能够更持久一些,业务代码发生变化时就只会影响到自己的bundle变化。
webpack.config.js增加如下配置:
optimization:{
splitChunks: {
cacheGroups: {
vendor:{//将第三方库拆分出来
name:'vendor',
test:/[\\/]node_modules[\\/]/,
minSize:0,//最小大小设置成0,把所有的依赖都给提取出来变成独立的bundle
minChunks:1,//模块至少使用次数,当值为2时,代表只引用了一次的模块不做分割打包处理
priority:10,//值越大优先级越高
chunks:'initial'
}
}
}
},
拆分前app的bundle:
拆分后app的bundle体积缩小了很多。同时生产了新的vendor.bundle.js,这里实际上的业务逻辑在app.bundle.js中只有44.3k,剩下的vendor.bundle.js都是我们引入的第三方依赖:
接着配置抽取公共业务代码部分:
optimization:{
splitChunks: {
cacheGroups: {
vendor:{//将第三方库拆分出来
name:'vendor',
test:/[\\/]node_modules[\\/]/,
minSize:0,//最小大小设置成0,把所有的依赖都给提取出来变成独立的bundle
minChunks:1,// (默认值:1)在拆分之前共享模块的最小块数
priority:10,//值越大优先级越高
chunks:'initial'// 共有3个值"initial","async"和"all"。initial就是同步加载的模块,async是异步加载的模块,这里的同步和异步就是静态或动态引入模块的方式,all就包含了前面两种。
},
common:{//提取业务代码中公共的部分
name:'common',
test:/[\\/]src[\\/]/,
chunks:'all',
minSize:0,
minChunks:2
}
}
}
},
但是如果我们的业务本身比较复杂,而且工程比较大,我们的bundle还是会比较大,此时我们可以动态加载我们的代码。
比如:
import {add} from './math';
console.log(add(1,2))
改成动态异步加载:
import('./math').then(math => {
console.log(math.add(1,2))//加载完成后再做的逻辑处理
})
当然不同的框架都有自己的动态加载的解决方案,webpack也有提供动态加载的解决方案。
比如本次案列中对所有的card进行动态的加载,如果开始我们还没有用到card我先不加载,只有当card被使用到的时候才动态的现加载它,加载完成后才进行相关的逻辑处理,这里我们通过react提供的lazy和suspense就可以实现。
//将Card组件改成懒加载动态引入的方式
// import Card from './Card.jsx';
const Card = lazy(() => import('./Card'))
class Home extends React.Component {
render() {
let cards = []
cards.push(
model.map((panel) => (
<Suspense fallback={<div>loading...</div>}>
<Card
key={panel.name}
image={panel.image}
title={panel.name}
route={panel.route}
description={panel.body}
/>
</Suspense>
))
)
return <main className={this.props.classes.root}>{cards}</main>
}
}
动态加载前的打包:
动态加载后的打包:
多出来两个新的bundle 0和1