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
(mods) => <Component {...mods} {...props} />
這個函數嘛這樣稍微拆解後,是否是就很清晰了呢,回過頭來看一下咱們的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