react 异步加载组件

angular中, 异步加载组件时, 需要加载完mod.js之后使用$provide来将mod注册到angular全局, 才能实现异步组件中指令的加载和识别. 而在react中, 组件原本就是js函数, 理论上比angular中异步加载组件简单得多. 然而复杂的地方在于与webpack的结合.

首先, 在ES6中, import是导入export模块的语法, import()可以用作动态导入模块, 但是webpack在使用import()时, 底层调用的是require, 所以需要在模块底下加上require的导出语法.

1
2
3
4
5
6
7
const Demo = () => (
<div>
<span>Demo!</span>
</div>
);
module.exports = Demo; //这个是动态模块需要加上的
export default Demo;

然后是, webpack对动态模块的支持性. 当直接在import()中给定url时, webpack会在编译时分析所加载文件, 并将其打包为单独的文件. 如果url为动态内容或者是从后台获取到的内容, 那么就会存在问题.

webpack官方文档时, 可以看到它是支持动态模块的, 但是语法和例子都没写清楚. 不过后来在网上查资料时, 看到了Sean Larkin(webpack Maintainer)在知乎上的一个回答, 所以解决了这个问题.

1
const getTemplate = (templateName, templatePath) => import(`../../../../project-template/pc/${templatePath)/js/common/${templateName}.html`)

简单理解就是, 当在webpack中使用动态导入时, 需要给定partial path, 这样webpack才能找到你所需的模块.

以上两个问题解决以后, 实现一个异步加载的组件就比较简单了.

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import React from 'react';
import { PropTypes as P } from 'prop-types';

import Loading from './../loading';

class AsyncComponent extends React.PureComponent {
static propTypes = {
src: P.string.isRequired,
loadingStart: P.func,
loadingEnd: P.func,
};
static defaultProps = {
loadingStart: () => {},
loadingEnd: () => {},
};
constructor(props) {
super(props);
if (!props.src) {
throw 'AsyncComponent need src';
}
this.state = {
component: null,
};
}
getComponentFromFileAsync(src) {
this.props.loadingStart();
import(`./../../_container/${src}.js`).then((v) => {
if (!this._isMounted) return;
this.props.loadingEnd();
this.setState({
component: v,
});
});
}

componentDidMount() {
this._isMounted = true;
this.getComponentFromFileAsync(this.props.src);
}
componentWillReceiveProps(nextProps, nextState) {
if (nextProps.src != this.props.src) {
this.getComponentFromFileAsync(nextProps.src);
}
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
let Com = this.state.component;
if (!Com) {
return <Loading />;
} else {
return <Com {...this.props} src={undefined} />;
}
}
}

export default AsyncComponent;

推荐配合router一起使用, 也可做动态配置的权限控制, 使用方法 :

1
2
3
4
5
6
7
8
9
10
11
12
13
<Switch>
{this.state.routeList.map((item, index) => {
if (item.get('to')) {
return <Redirect key={index} path={item.get('url')} exact={item.get('exact')} to={item.get('to')} />;
} else if (item.get('src')) {
return <Route key={index} path={item.get('url')} exact={item.get('exact')} render={() => <AsyncComponent src={item.get('src')} />} />;
} else {
return null;
}
})}
<Route exact path='/' render={() => '欢迎来到首页'} />
<Redirect path='*' to='/' />
</Switch>
作者

Mosby

发布于

2017-10-13

许可协议

评论