react异步加载组件实现解析

react+webpack对于react开发,相信已经是一个大众套餐了,至于其他的parcel或者rollup或者其他一些构建框架我也没仔细用过,也不太熟,听说parcel上github短时间内已经上万颗星了,很流弊的样纸,不过这都不是我们本文重点,呵呵。本文重点是模块的异步加载,这里不谈其他的,只谈谈按需加载优化。使用webpack构建的时候,我们会把公共资源打到vendor文件中去,以便让我们的业务文件瘦身,但是我们的业务可能会有大量子业务,可能某些业务在某些用户那里永远都不会用到,这种情况下,如果我们把所有业务代码一次性全部加载下来,不是太浪费太奢侈了吗,好在webpack自身提供了require.ensure()函数,不过,这种优化方式又不在我们本次探讨范围内,本次我们来介绍一下本人心目中比较高大上的react异步加载组件。就算不喜欢,学习一下也是极好的事情啦。 我们来看以下这段代码,若不采用异步模块加载,page1和page2会合并到一个业务文件中,我要是永远不进入/buy路由,这不是浪费加载吗,那page2最好还是做成异步组件吧,那具体应该怎么做呢?

<Route path="/" component={App}>
		<IndexRoute component={page1}/>
		<Route path="/buy" component={page2}/>
	</Route>
复制代码

好咯,不就是个异步组件吗,那不是很简单的吗,不就类似下面这样就行了的:

let page2 = ()=>{
    let page2Comp = import('lazyComp');
    return {
        <div>
            <page2Comp />
        </div>
    }
}
复制代码

哎呀嘛,这智商也是忒高了,以为自己多流弊啊,寥寥几行就实现了一个异步组件,天真!可惜就是报错了。不知道为啥?看看import()这玩意儿返回的是啥好不好,人家返回的是个promise对象,至少得先处理一下才好吧。这就好比你请人家上你家吃酒席,你至少得先安排人位子啊,先拿条椅子占个坑吧,等到人家来了才想起来搬凳子,任谁都不开心,掉头就走啦。那怎么给占个坑呢?其实很简单的道理,大家肯定都很熟悉的啦,请看下面一个小栗子。

class MyComp extends React.Component{
    constructor(){
        return {
            isLoaded:false
        }
    }
    render(){
        let { isLoaded, data } = this.state;
        if(!isLoaded){
            return null;
        }
        return <Wrapper data={data} />
    }
    componentDidMount(){
        http.getData({}).then((results)=>{
            this.setState({
                data:results.data,
                isLoaded:true
            })
        })
    }
}
复制代码

这段代码大家都挺熟悉了吧,数据没返回之前,不做具体渲染,直到数据返回,才开始渲染。只不过异步组件在这里有所区别的是,获取的数据就是组件本身,组件未获取到前怎么办呢?简单,用一个空元素占个位不就OK了嘛。接下来我们来看一下webpack官网给出来的一个异步组件实栗:

class LazilyLoad extends React.Component {

	constructor() {
		super(...arguments);
		this.state = {
			isLoaded: false,
		};
	}

	componentDidMount() {
		this._isMounted = true;
		this.load();
	}

	componentDidUpdate(previous) {
		if (this.props.modules === previous.modules) return null;
		this.load();
	}

	componentWillUnmount() {
		this._isMounted = false;
	}

	load() {
		this.setState({
			isLoaded: false,
		});

		const { modules } = this.props;
		const keys = Object.keys(modules);

		Promise.all(keys.map((key) => modules[key]()))
			.then((values) => (keys.reduce((agg, key, index) => {
				agg[key] = values[index];
				return agg;
			}, {})))
			.then((result) => {
				if (!this._isMounted) return null;
				this.setState({ modules: result, isLoaded: true });
			});
	}

	render() {
		if (!this.state.isLoaded) return <div className="toast toast-show">
			<Loading/>
		</div>;
		console.log("modules:",this.state.modules);
		return React.Children.only(this.props.children(this.state.modules));
	}
}
复制代码

是不是觉得跟上面异步加载数据的栗子十分有血缘关系呢?接下来,我们具体来看一下这段代码,其他地方就不多说了,或许有些同学可能看不太明白render函数中的return React.Children.only(this.props.children(this.state.modules));这一句代码,这种渲染方式叫做回调渲染。稍微给大家做个分析,我们先来看看以上组件的调用示例代码:

const LazilyLoadFactory = (Component, modules) => {
	console.log("LazilyLoadFactory");
	return (props) => (
		<LazilyLoad modules={modules}>
			{(mods) => <Component {...mods} {...props} />}
		</LazilyLoad>
	);
};
复制代码

如果还是不太理解,先把上面return React.Children.only(this.props.children(this.state.modules));这句代码中的几个元素拆解一下:

  • this.props.children:这个大家应该都懂啥意思了,指代调用组件的子元素,以上示例中,不就是指代(mods) => <Component {...mods} {...props} />这个函数嘛
  • this.state.modules:这个就是LazilyLoad组件中传入props的modules变量被处理成state的变量
  • React.Children.only:就不必说了吧,想必都比较熟悉了

这样稍微拆解后,是不是就很清晰了呢,回过头来看一下我们的stateless组件LazilyLoadFactory,渲染的是LazilyLoad组件,使用回调渲染的方式实际上以参数modules作为props入参对参数组件Component进行渲染,那就很明显了,参数组件Component就是这个异步组件的占坑板凳了,我们来看看这个作为参数传入的组件的具体代码:

class WrapLazyComp extends React.Component{
	render(){
		const Comp = this.props.Comp;
		return <div>
			<Comp />
		</div>;
	}
}
复制代码

好了,然后接下来就是我们的总调用方了

LazilyLoadFactory(WrapLazyComp,{
	Comp: () => import('实际业务模块')
});
复制代码

到此为止,我们的异步组件整个就完成了,主要就是利用import()实现对模块的异步加载,可能有些同学对于回调渲染可能会有些模糊,不熟悉的可能稍微需要了解了解。 个人比较喜欢这种方式进行异步模块的加载,当然还有类似require.ensure等等此类的方法,具体优化方式视个人偏好以及项目具体情况,也不能一概而论。 好了,感谢各位,如有错误,请多多指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值