react-loadable是什麼?咱們首先看看官方說明javascript
A higher order component for loading components with dynamic imports.java
經過hoc給組件提供動態加載功能,在實際業務開發中,常常遇到性能優化的問題,其中有一個優化的點,就是經過動態加載組件來減小首屏的size,在react官方的lazy 和suspense出來以前,相信這是react最流行的動態加載的庫,包括如今也是,最近在研究next.js的過程當中,發現next的dynamic的實現也是基於這個庫,廢話很少說,接下來就讓咱們揭開react-loadable的神祕面紗react
這點官方文檔寫的很清楚,以下所示,Loadable的loader屬性中使用import來動態加載組件,同時其中的loading屬性是咱們可配置的loading的componentwebpack
import Loadable from 'react-loadable';
import Loading from './my-loading-component';
const LoadableComponent = Loadable({
loader: () => import('./my-component'),
loading: Loading,
});
export default class App extends React.Component {
render() {
return <LoadableComponent/>;
}
}
複製代碼
對,就是這麼快,上面已經講完1+1=2了,接下來咱們就能夠着手解決如何在晚上登陸太陽了,哈哈哈web
function Loadable(opts) {
return createLoadableComponent(load, opts);
}
複製代碼
如上所示,默認暴露出來的Loadable方法底層主要依賴了createLoadableComponent 和load方法,那咱們就先看看createLoadableComponent方法是幹什麼滴🤔,咱們一段段滴貼代碼,數組
if (!options.loading) {
throw new Error("react-loadable requires a `loading` component");
}
let opts = Object.assign(
{
loader: null,
loading: null,
delay: 200,
timeout: null,
render: render,
webpack: null,
modules: null
},
options
);
複製代碼
入口這裏基本就是常見的options值進行處理,其中這裏要求loading屬性必定要傳,畢竟這個是動態加載嘛,接下來是init方法的定義promise
let res = null;
function init() {
if (!res) {
res = loadFn(opts.loader);
}
return res.promise;
}
複製代碼
其中loadFn方法就是咱們在調用createLoadableComponent是傳進來的load,load方法咱們稍後會解釋到,在這裏咱們能夠稍微猜到,opts.loader是咱們在示例中傳進來的性能優化
() => import('./my-component')
複製代碼
這個方法會返回一個promise,resolve時的值就是咱們加載好的組件啦,接下來就是把init方法塞到ALL_INITIALIZERS和READY_INITIALIZERS兩個數組裏面,這是爲了在服務器渲染時,能夠預加載咱們的組件,塞到這兩個數組的組件會被promise.all來執行,如何使用,能夠看官方的demo,這裏不細展開服務器
ALL_INITIALIZERS.push(init);
if (typeof opts.webpack === "function") {
READY_INITIALIZERS.push(() => {
if (isWebpackReady(opts.webpack)) {
return init();
}
});
}
複製代碼
在完成這些時候,會返回一個class LoadableComponent extends React.Component
組件,咱們先看看這個組件的構造異步
constructor(props) {
super(props);
init();
this.state = {
error: res.error,
pastDelay: false,
timedOut: false,
loading: res.loading,
loaded: res.loaded
};
}
複製代碼
在這裏會調用init方法,以及初始化state,init上面咱們講到了會調用load方法,那麼咱們先跳到load方法看看
function load(loader) {
let promise = loader();
let state = {
loading: true,
loaded: null,
error: null
};
state.promise = promise
.then(loaded => {
state.loading = false;
state.loaded = loaded;
return loaded;
})
.catch(err => {
state.loading = false;
state.error = err;
throw err;
});
return state;
}
複製代碼
load中首先調用loader方法,這個方法就是
() => import('./my-component')
複製代碼
而後在then方法中把加載回來的組件賦值給內部的state的loaded,同時把loading置爲false,最後把state返回去,state.promise的就是組件加載的promise,只要這個promise被resolve了,那麼就是組件加載完畢了,OK,load講解完畢,咱們返回到咱們的LoadableComponent組件繼續講解
在componentWillMount生命週期中,首先會經過res判斷組件是否正在加載,res是init方法執行後返回的state,若是組件不是正在加載,那麼直接return,不然設置組件加載delay和timeout的定時器,接下來到重點了,對res.promise添加then來添加監聽組件是否加載完畢,當組件加載完畢的話,會執行update方法
let update = () => {
if (!this._mounted) {
return;
}
this.setState({
error: res.error,
loaded: res.loaded,
loading: res.loading
});
this._clearTimeouts();
};
複製代碼
update方法中會更新state的狀態,其中loaded是異步加載回來的組件,最後咱們看看render方法
render() {
if (this.state.loading || this.state.error) {
return React.createElement(opts.loading, {
isLoading: this.state.loading,
pastDelay: this.state.pastDelay,
timedOut: this.state.timedOut,
error: this.state.error,
retry: this.retry
});
} else if (this.state.loaded) {
return opts.render(this.state.loaded, this.props);
} else {
return null;
}
}
複製代碼
組件還沒加載回來時loading爲true,則會加載loading組件,當組件加載完畢時,此時this.state.loaded 爲true,這時就會渲染咱們異步加載回來的組件,至此,react-loadable分析完畢
經過一步步分析下來,咱們能夠發現原理很簡單,在組件沒加載回來以前,先渲染loading組件,當組件加載完畢,經過改變state的值從新渲染,從而加載咱們原來想要的組件,emmmm,就是這個簡單