react-loadable是什么?我们首先看看官方说明
A higher order component for loading components with dynamic imports.
通过hoc给组件提供动态加载功能,在实际业务开发中,经常遇到性能优化的问题,其中有一个优化的点,就是通过动态加载组件来减少首屏的size,在react官方的lazy 和suspense出来之前,相信这是react最流行的动态加载的库,包括现在也是,最近在研究next.js的过程中,发现next的dynamic的实现也是基于这个库,废话不多说,接下来就让我们揭开react-loadable的神秘面纱
如何使用
这点官方文档写的很清楚,如下所示,Loadable的loader属性中使用import来动态加载组件,同时其中的loading属性是我们可配置的loading的component
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了,接下来我们就可以着手解决如何在晚上登录太阳了,哈哈哈
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方法的定义
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,就是这个简单