最近在學習react,以前作的一個項目首屏加載速度很慢,便蒐集了一些優化方法,react-loadable
這個庫是我在研究路由組件按需加載的過程當中發現的,其實react-router v4
官方也有code splitting
的相關實踐,可是在如今的webpack 3.0版本下已經不行了,由於須要使用如下相關語法react
import loadSomething from 'bundle-loader?lazy!./Something'
複製代碼
有興趣的同窗能夠自行研究。webpack
react-router-v4:code-splittinggit
後面就發現了react-loadable這個庫能夠幫助咱們按需加載,其實和react-router-v4官方實現的原理差不太多,基本使用方法以下:es6
第一步:先準備一個Loding組件,這個是官方的,你本身寫一個更好:github
const MyLoadingComponent = ({ isLoading, error }) => {
// Handle the loading state
if (isLoading) {
return <div>Loading...</div>;
}
// Handle the error state
else if (error) {
return <div>Sorry, there was a problem loading the page.</div>;
}
else {
return null;
}
};
複製代碼
第二步:引入react-loadableweb
import Loadable from 'react-loadable';
const AsyncHome = Loadable({
loader: () => import('../containers/Home'),
loading: MyLoadingComponent
});
複製代碼
第三步:替換咱們本來的組件bash
<Route path="/" exact component={AsyncHome} />
複製代碼
這樣,你就會發現只有路由匹配的時候,組件才被import進來,達到了code splitting
的效果,也就是咱們常說的按需加載,代碼分塊,而不是一開始就將所有組件加載。react-router
能夠觀察到,點擊不一樣的路由都會加載一個chunk.js,這就是咱們所分的塊。異步
不要把 import
關鍵字和import()
方法弄混了,該方法是爲了進行動態加載才被引入的。ide
import
關鍵字的引入是靜態編譯且存在提高的,這就對咱們按需加載產生了阻力(畫外音:require是能夠動態加載的),因此纔有了import()
,而react-loadable
即是利用了import()
來進行動態加載。
並且這個方法不能傳變量,只能使用字符串和字符串模板,本來想將那一堆生成組件的代碼進行抽象,結果死活不行,才發現坑在這裏。
react-loadable
有5k star,內部機制已經十分完善了,看如今的源碼我確定看不懂,因而誤打誤撞地看了其initial commit
的源碼。
咱們上面對Loadable
函數的用法是這樣的:
const AsyncHome = Loadable({
loader: () => import('../containers/Home'),
loading: MyLoadingComponent
});
複製代碼
接收一個配置對象爲參數,第一個屬性名爲loader
,是一個方法,用於動態加載咱們所須要的模塊,第二個參數就是咱們的Loading
組件咯,在動態加載還未完成的過程當中會有該組件佔位。
{
loader: () => import('../containers/Home'),
loading: MyLoadingComponent
}
複製代碼
這個方法的返回值是一個react component,咱們Route
組件和url香匹配時,加載的就是這個component,該component經過loader方法進行異步加載組件以及錯誤處理。其實就這麼多,也有點高階組件的意思。
而後來看看源碼吧(源碼參數部分使用了ts進行類型檢查)。
import React from "react";
export default function Loadable( loader: () => Promise<React.Component>, LoadingComponent: React.Component, ErrorComponent?: React.Component | null, delay?: number = 200 ) {
// 有時組件加載很快(<200ms),loading 屏只在屏幕上一閃而過。
// 一些用戶研究已證明這會致使用戶花更長的時間接受內容。若是不展現任何 loading 內容,用戶會接受得更快, 因此有了delay參數。
let prevLoadedComponent = null;
return class Loadable extends React.Component {
state = {
isLoading: false,
error: null,
Component: prevLoadedComponent
};
componentWillMount() {
if (!this.state.Component) {
this.loadComponent();
}
}
loadComponent() {
// Loading佔位
this._timeoutId = setTimeout(() => {
this._timeoutId = null;
this.setState({ isLoading: true });
}, this.props.delay);
// 進行加載
loader()
.then(Component => {
this.clearTimeout();
prevLoadedComponent = Component;
this.setState({
isLoading: false,
Component
});
})
.catch(error => {
this.clearTimeout();
this.setState({
isLoading: false,
error
});
});
}
clearTimeout() {
if (this._timeoutId) {
clearTimeout(this._timeoutId);
}
}
render() {
let { error, isLoading, Component } = this.state;
// 根據狀況渲染Loading 所需組件 以及 錯誤組件
if (error && ErrorComponent) {
return <ErrorComponent error={error} />;
} else if (isLoading) {
return <LoadingComponent />;
} else if (Component) {
return <Component {...this.props} />;
}
return null;
}
};
}
複製代碼
安利一下我正在練習的項目react-music
參考資料:
webpack v3 結合 react-router v4 作 dynamic import — 按需加載(懶加載)