共计 3488 个字符,预计需要花费 9 分钟才能阅读完成。
react-loadable 源码解析
简要的来说, loadable 是一个高阶函数, 他同时利用了 react 的渲染 API, webpack 知识点, babel, promise 合并起来的组件
应用
首先咱们要晓得 react-loadable
他的用法是什么:
- loader
须要提早加载的组件, 应用必须要用() => import('xxx')
语法 - loading
loading 组件, props 承受 error, pastDelay , timedOut, retry 参数, 能够自定义 - delay
能够增加提早 - timeout
超时工夫 - render
类型为: (loaded, props)=>ReactNode, 能够增加额定的参数注入
多个组件同时加载
其中承受的参数 loader, render, 类型和上述差不太多
Loadable.Map({
loader: {Bar: () => import('./Bar'),
i18n: () => fetch('./i18n/bar.json').then(res => res.json()),
},
render(loaded, props) {
let Bar = loaded.Bar.default;
let i18n = loaded.i18n;
return <Bar {...props} i18n={i18n}/>;
},
})
预加载
const LoadableBar = Loadable({loader: () => import('./Bar'),
loading: Loading,
});
触发:
LoadableBar.preload();
库里还波及到 SSR 相干知识点, 这里就不波及了
源码
这里因为不讲 SSR 相干, 所以我在这里删除了相干代码: loadable.jsx
主体
在此高阶组件中, 他的主体是: createLoadableComponent
首先咱们看他闭包中所作的货色:
function createLoadableComponent(loadFn, options) {
// loading 的判断, 疏忽
// 创立配置项, 笼罩默认值
// 其中 render 源码: function render(loaded, props) {// return React.createElement(resolve(loaded), props);
// }
let opts = Object.assign(
{
loader: null,
loading: null,
delay: 200,
timeout: null,
render: render,
webpack: null,
modules: null
},
options
);
// 后果, 用于 调用 loader
let res = null;
// 初始化时调用, loadFn 函数前面再讲
function init() {if (!res) {res = loadFn(opts.loader);
}
return res.promise;
}
return class LoadableComponent extends React.Component{// 这里先疏忽}
}
返回组件
咱们再来看返回的组件:
class LoadableComponent extends React.Component {constructor(props) {super(props);
init(); // 在构造函数中启用初始化函数, 他将 res 赋值为一个 promise
// 定义的 state
this.state = {
error: res.error,
pastDelay: false,
timedOut: false,
loading: res.loading,
loaded: res.loaded
};
}
// 动态函数, 之前介绍用法的时候说过了
static preload() {return init();
}
componentWillMount() {
// 用来设置定时器和 delay 相干
this._loadModule();}
componentDidMount() {
// 标记是否 mounted
this._mounted = true;
}
componentWillUnmount() {
// 批改标记, 革除定时器
this._mounted = false;
this._clearTimeouts();}
render() {
// 渲染函数, 如果以后是 加载中或者谬误加载的状态 , 则应用 loading 渲染, 并且传递多种参数
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) {
// 如果曾经加载结束, 则调用 render 函数, 应用 React.createElement 渲染
return opts.render(this.state.loaded, this.props);
} else {return null;}
}
}
load
// 这里的 load 就是 createLoadableComponent(loadFn, options) 中的入参 loadFn
function load(loader) {
// loader 是 options 中的 loader
// 比方: () => import('./my-component')
let promise = loader();
// 用来返回后果
let state = {
loading: true,
loaded: null,
error: null
};
// 一个 promise 赋值, 未调用
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;
}
loadMap
调用:
Loadable.Map = LoadableMap;
function LoadableMap(opts) {return createLoadableComponent(loadMap, opts);
}
具体代码:
function loadMap(obj) {
let state = {
loading: false,
loaded: {},
error: null
};
let promises = [];
try {Object.keys(obj).forEach(key => {let result = load(obj[key]);
if (!result.loading) {state.loaded[key] = result.loaded;
state.error = result.error;
} else {state.loading = true;}
promises.push(result.promise);
result.promise
.then(res => {state.loaded[key] = res;
})
.catch(err => {state.error = err;});
});
} catch (err) {state.error = err;}
state.promise = Promise.all(promises)
.then(res => {
state.loading = false;
return res;
})
.catch(err => {
state.loading = false;
throw err;
});
return state;
}
总的来说, 和 load 相似, 利用了 Promise.all
api 来构建了一个 promise 数组后果
总结
从组件来上看构造:
Loadable()
=== createLoadableComponent(load, opts)
=== class LoadableComponent
从调用上来看:
init
调用load
函数, 他用来包装 组件加载后的参数- init 间接返回组件对应 promise 的后果
- 在 render 函数中依据对应后果渲染 loading 组件或者 render 组件
- render 组件利用的是
React.createElement
组件渲染
总的来说去除 SSR 相干, 还是一个比较简单的组件, 次要的利用还是 ()=>import()
语法的反对
参考:
https://github.com/jamiebuild…
正文完