zp1112 / blog

地址
http://issue.suzper.com/
36 stars 3 forks source link

如何异步加载组件 #13

Open zp1112 opened 6 years ago

zp1112 commented 6 years ago

异步组件加载

我们知道react或者是其他用于写单页应用的框架都是组件化的概念,每个路由每个页面就是一个个大组件,webpack在打包的时候,将所有的文件都打包进一个bundle里面,但是我们往往在a页面的时候不需要加载b页面的东西,理想情况下,用户访问一个页面时,该页面应该只需要加载自己使用到的代码,为了提高性能,webpack支持代码分片,将js代码打包到多个文件中按需加载。 按需加载的方式有两种,一个是 webpack提供的require.ensure(),一个是 ES6提案的import() 下面我们写一个asyncComponent异步加载方法,分别使用这两种方式实现。其实是写了一个高阶组件,高阶组件的理解可以看这篇文章

webpack提供的require.ensure()

webpack提供了require.ensure(),webpack 在编译时,会静态地解析代码中的 require.ensure(),同时将模块添加到一个分开的 chunk 当中。这个新的 chunk 会被 webpack 通过 jsonp 来按需加载。

// asyncCmponent.js
import React, { Component } from 'react';

export default function asyncComponent (importFunc) {
  return class App extends Component {
    constructor(props) {
      super(props);
      this.state = {
        component: null
      }
    };
    componentDidMount = () => {
      importFunc().then(mod => {
        this.setState({
          component: mod.default || mod
        })
      });
    }
    render = () => {
      const C = this.state.component;
      return (
        C ? <C {...this.props} /> : null
      )
    }
  }
}

调用

import asyncCmponent from './asyncCmponent.js';

const App = asyncCmponent(() => require.ensure([], (require) => require('./App')));
ReactDOM.render(
  <Provider store={store}>
      <App />
   </Provider>, 
   document.getElementById('root')
);

打开浏览器,可以看到除了bundle.js还多了个1.js,且bundle的体积被拆分出来了。 image

ES6提案的import()

es6中我们知道import是用于加载使用export命令定义的模块,import和require的区别在于import是在静态解析阶段执行的,所以它是一个模块之中最早执行的,而require是动态加载的,运行时加载模块,import命令无法取代require的动态加载功能。require到底加载哪一个模块,只有运行时才知道。import命令做不到这一点。因此,有一个提案,建议引入import()函数,完成动态加载。详情请看《es6入门》 import()函数返回的是一个promise。

// 只需修改componentDidMount部分
componentDidMount = () => {
      importFunc().then(mod => {
        this.setState({
          component: mod.default || mod
        })
      })
    }

调用

const App = asyncCmponent(() => import('./App')));

打开浏览器,可以看到同样的效果。boom!!!

meibin08 commented 5 years ago

很棒!