react異步加載組件實現解析

react+webpack對於react開發,相信已是一個大衆套餐了,至於其餘的parcel或者rollup或者其餘一些構建框架我也沒仔細用過,也不太熟,據說parcel上github短期內已經上萬顆星了,很流弊的樣紙,不過這都不是咱們本文重點,呵呵。本文重點是模塊的異步加載,這裏不談其餘的,只談談按需加載優化。使用webpack構建的時候,咱們會把公共資源打到vendor文件中去,以便讓咱們的業務文件瘦身,可是咱們的業務可能會有大量子業務,可能某些業務在某些用戶那裏永遠都不會用到,這種狀況下,若是咱們把全部業務代碼一次性所有加載下來,不是太浪費太奢侈了嗎,好在webpack自身提供了require.ensure()函數,不過,這種優化方式又不在咱們本次探討範圍內,本次咱們來介紹一下本人心目中比較高大上的react異步加載組件。就算不喜歡,學習一下也是極好的事情啦。 咱們來看如下這段代碼,若不採用異步模塊加載,page1和page2會合併到一個業務文件中,我要是永遠不進入/buy路由,這不是浪費加載嗎,那page2最好仍是作成異步組件吧,那具體應該怎麼作呢?css

<Route path="/" component={App}>
		<IndexRoute component={page1}/>
		<Route path="/buy" component={page2}/>
	</Route>
複製代碼

好咯,不就是個異步組件嗎,那不是很簡單的嗎,不就相似下面這樣就好了的:react

let page2 = ()=>{
    let page2Comp = import('lazyComp');
    return {
        <div>
            <page2Comp />
        </div>
    }
}
複製代碼

哎呀嘛,這智商也是忒高了,覺得本身多流弊啊,寥寥幾行就實現了一個異步組件,天真!惋惜就是報錯了。不知道爲啥?看看import()這玩意兒返回的是啥好很差,人家返回的是個promise對象,至少得先處理一下才好吧。這就比如你請人家上你家吃酒席,你至少得先安排人位子啊,先拿條椅子佔個坑吧,等到人家來了纔想起來搬凳子,任誰都不開心,掉頭就走啦。那怎麼給佔個坑呢?其實很簡單的道理,你們確定都很熟悉的啦,請看下面一個小栗子。webpack

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官網給出來的一個異步組件實慄:git

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));這一句代碼,這種渲染方式叫作回調渲染。稍微給你們作個分析,咱們先來看看以上組件的調用示例代碼:github

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));這句代碼中的幾個元素拆解一下:web

  • 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>;
	}
}
複製代碼

好了,而後接下來就是咱們的總調用方了promise

LazilyLoadFactory(WrapLazyComp,{
	Comp: () => import('實際業務模塊')
});
複製代碼

到此爲止,咱們的異步組件整個就完成了,主要就是利用import()實現對模塊的異步加載,可能有些同窗對於回調渲染可能會有些模糊,不熟悉的可能稍微須要瞭解瞭解。 我的比較喜歡這種方式進行異步模塊的加載,固然還有相似require.ensure等等此類的方法,具體優化方式視我的偏好以及項目具體狀況,也不能一律而論。 好了,感謝各位,若有錯誤,請多多指教。bash

相關文章
相關標籤/搜索