为什么要代码分割
日常我们会通过 webpack 打包我们的应用,产生一个 bundle.js 文件。随着我们的项目越写越复杂,bundle.js 文件会随之增大。 由于该文件是唯一的,所以不管用户查看哪个页面、使用哪个功能,都必须先下载所有的功能代码。 当 bundle.js 大到一定程度,就会明显影响用户体验。此时,我们就需要 code splitting ,将代码分片,实现按需异步加载,从而优化应用的性能。
核心原理
关键字import(es规范草案
)可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise。
Webpack会自动据此完成代码分片(chunk),不需要任何额外的手动配置
1 | import('./math').then(res=>{ |
以路由为中心进行代码分片
- 通过高阶组件函数返回一个React.Component
路由匹配成功则立即调用import()并展示组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34function AsyncComponent(loadComponent,placeholder='loading……'){
return class AyncComponent extends React.Component{
state = {
Child:null
}
async componentDidMount(){
loadComponent().then(res=>{
this.setState({
Child:res.default
})
})
}
render(){
const { Child } = this.state;
return (
Child ? <Child {...this.props} /> : placeholder
);
}
}
}
const App = ()=>{
return (
<Router>
<Route path="/m1" component={AsyncComponent(()=>{
return import('./pages/m1');
})} />
<Route path="/m2" component={AsyncComponent(()=>{
return import('./pages/m2');
})} />
</Router>
)
}
ReactDOM.render(<App />, document.getElementById('root'));
以组件为中心进行代码分片
路由本身并没有什么特别的,它也是组件。如果以组件为中心进行代码分版,会带来额外的好处:
- 同一个组件中,针对不着急显示的部分,可以延迟其加载。
- 分割点不再局限于路由,可以任意指定
- 与路由匹配成功则加载不同,可以自行指定加载时机
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40function DelayComponent(loadComponent,delay=2000,placeholder='loading……'){
return class AyncComponent extends React.Component{
state = {
Child:null
}
async componentDidMount(){
this.timer = setTimeout(() => {
loadComponent().then(res=>{
this.setState({
Child:res.default
})
})
}, delay);
}
componentWillUnmount(){
this.timer && clearTimeout(this.timer);
}
render(){
const { Child } = this.state;
return (
Child ? <Child {...this.props} /> : placeholder
);
}
}
}
const DelayM1Child = DelayComponent(()=>import('./m1Child'),2000);
export default class M1 extends React.Component {
render() {
return (
<div>
<div>m111111</div>
<DelayM1Child />
</div>
)
}
}