首先需要知道的是dynamic import通过返回Promise的方式实现异步加载功能。
import('./component.js')
.then((m) => {
// 处理异步加载到的模块m
})
.catch((err) => {
// 错误处理
});
要注意的是import的参数不能使用变量,简单原则是至少要让Webpack知晓应该预先加载哪些内容。这里的参数除了使用常量之外,还可以使用模板字符串componentDir/${name}.js
。
其实到这里基本完成代码切割了,接下来做得就是结合react-router实现按模块异步加载。这是跟业务代码相关的,因此每个人的做法都是不一样的。所以以下代码仅供参考。
异步加载
我参考react-router的例子写了个简单的异步加载组件AsyncLoader.js,内容:
import React from 'react';
export default class AsyncLoader extends React.Component {
static propTypes = {
path: React.PropTypes.string.isRequired,
loading: React.PropTypes.element,
};
static defaultProps = {
path: '',
loading: <p>Loading...</p>,
error: <p>Error</p>
};
constructor(props) {
super(props);
this.state = {
module: null
};
}
componentWillMount() {
this.load(this.props);
}
componentWillReceiveProps(nextProps) {
if (nextProps.path !== this.props.path
|| nextProps.error !== this.props.error
|| nextProps.loading !== this.props.loading) {
this.load(nextProps);
}
}
load(props) {
this.setState({module: props.loading});
// TODO:异步代码的路径希望做成可以配置的方式
import(`./path/${props.path}`)
.then((m) => {
let Module = m.default ? m.default : m;
console.log("module: ", Module);
this.setState({module: <Module/>});
}).catch(() => {
this.setState({module: props.error});
});
}
render() {
return this.state.module;
}
}
使用方法
<Route
exact path='/book'
render={()=><AsyncLoader path={'./components/Book.js'}/>}
/>
Webpack打包的时候会根据import的参数生成相应的js文件,默认使用id(webpack生成的,从0开始)命名这个文件。
const Search = asyncComponent(() => import(/* webpackChunkName: "search" */ "./containers/Search/SearchContainer"))
const BookList = asyncComponent(() => import(/* webpackChunkName: "bookList" */ "./containers/BookList/BookListContainer"))
import React from 'react'
export const asyncComponent = loadComponent => (
class AsyncComponent extends React.Component {
state = {
Component: null,
}
componentWillMount() {
if (this.hasLoadedComponent()) {
return;
}
loadComponent()
.then(module => module.default) ////兼容 module.default ? module.default : module
.then((Component) => {
this.setState({ Component });
})
.catch((err) => {
console.error(`Cannot load component in <AsyncComponent />`);
throw err;
});
}
hasLoadedComponent() {
return this.state.Component !== null;
}
render() {
const { Component } = this.state;
return (Component) ? <Component {...this.props} /> : null;
}
}
);