性能优化系列之-代码分割

416 阅读2分钟

为什么要代码分割?

我们经常会通过 webpack 打包我们的应用,产生一个 bundle.js 文件。随着我们的项目越写越复杂,bundle.js 文件会随之增大,严重影响了首屏加载速度。

由于该文件是唯一的,所以不管用户查看哪个页面、使用哪个功能,都必须先下载所有的功能代码。 当 bundle.js 大到一定程度,就会明显影响用户体验。此时,我们就需要 code splitting ,将代码分片,实现按需异步加载,从而优化应用的性能。

import('./math').then(res=>{
    console.log(res.default)
});
import('./print').then(res=>{
    res.default('from print')
});
dist
├── 0.bundle.js
├── 0.bundle.js.map
├── 1.bundle.js
├── 1.bundle.js.map
├── bundle.js
├── bundle.js.map
└── index.html

以路由为中心进行代码分片

  • 通过高阶组件函数返回一个React.Component
  • 路由匹配成功则立即调用import()并展示组件

function 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'));

以组件为中心进行代码分片

路由本身并没有什么特别的,它也是组件。如果以组件为中心进行代码分版,会带来额外的好处:

  • 同一个组件中,针对不着急显示的部分,可以延迟其加载。
  • 分割点不再局限于路由,可以任意指定
  • 与路由匹配成功则加载不同,可以自行指定加载时机

function 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>
    )
  }
}

demo 地址:

codesandbox.io/s/gifted-sw…