2六、異步組件
當在React裏使用異步組件時,核心知識是兩個:react
webpack 如何異步加載其餘模塊:經過 require(['xxx'], function(module){})來實現;
React 裏如何使用異步加載的這個模塊:參考正常使用模塊時的作法;webpack
【異步加載】web
關於 webpack 的異步加載,能夠查看我寫的這一篇異步加載實戰DEMO.數組
簡單來講,就是 require 的參數一,從字符串變爲數組,而後參數二是一個回調函數,函數的參數,就是你異步加載的模塊。app
所以 拿到參數 等於 得到模塊。異步
【React裏如何使用】async
咱們要異步得到這個模塊;
咱們能夠參考高階組件的用法,來使用這個模塊(函數返回一個類,賦值給某個變量,而後該變量做爲JSX的標籤使用);
先給一個簡單版本:函數
class RefsDemo extends React.Component {
constructor() {
super()
this.state = {
myComponent: null
}
this.load = this.load.bind(this)
}ui
render() {
return <div>
{/* 點擊執行 load 方法 */}
<button onClick={this.load}>點擊加載異步組件</button>
{/* 變量存在時(非空,使用標籤做爲JSX的標籤名(該變量已被賦值異步模塊);不然使用null(即無DOM) */}
{
this.state.myComponent ? <this.state.myComponent></this.state.myComponent> : null
}
</div>
}this
load() {
// 這是一個異步行爲,因此須要在回調函數裏獲取這個模塊
require(['./learner.js'], Component => {
// 賦值給 state 變量
this.setState({
// 加載到的模塊存儲在 Comment.default 中(由於是經過 export default 導出的)
myComponent: Component.default
})
})
}
}
思路是:
用 state 變量 myComponent 存儲模塊,初始爲空(不顯示也不加載);
當點擊按鈕時,異步加載模塊 './learner.js',回調函數傳參得到該模塊;
將該模塊賦值給 state 變量 myComponent,觸發 state 改變時的生命週期(會觸發render方法);
render 從新渲染時,發現 this.state.myComponent 隱式轉換後非 false,所以使用其 JSX 標籤(即將異步組件嵌入到當前組件中);
因而將異步組件嵌入了當前組件中,實現了 React 組件的異步加載;
其餘的比較好理解,比較彆扭的是 JSX 語法:
{
this.state.myComponent ? <this.state.myComponent></this.state.myComponent> : null
}
之因此能夠這麼寫,參考本系列的 【22】 的第一小節,即組件被賦值給對象的屬性時(這裏體現的是 this 的 state 屬性的 myComponent 屬性),所以不須要大寫,能夠直接用變量名做爲標籤名。
進階——異步組件加載器:
問題:
以上的寫法仍是太過於複雜;
須要用 state 屬性來控制組件是否顯示;
須要用一個變量存儲該模塊,並在JSX語法裏用這個變量做爲標籤名;
要寫一個 load 方法,用於加載異步組件;
總而言之,不夠智能,不優雅;
目標:
寫一個異步組件加載器;
實現如下功能:
給其傳一個函數,如:const Learner = resolve => require(['./learner.js'], resolve),這個函數的原型是上面的 require(['./learner.js'], Component => {});
再給其傳一個變量 displayComponent,用於控制這個組件是否顯示;
當第一次設置 displayComponent 爲 true,且組件未加載時,則加載該組件;
爲了防止組件重複加載,所以組件內部變量 this.state.amount 負責表示當前組件狀態(未加載,加載中,加載完畢);
組件什麼時候顯示:組件加載完畢(this.state.component) && 父組件控制該組件是否顯示(this.state.displayComponent);
所以,父組件只須要幹兩件事情就好了:
傳一個柯里化處理後的異步組件加載函數;
一個變量 displayComponent 控制該異步組件是否顯示(首次顯示時自動加載);
如
代碼:
app.js 父組件內的代碼
// 引入異步組件加載器
import AsyncLoad from './asyncLoader.js'
// 異步組件加載函數封裝
const Leaner = resolve => require(['./learner.js'], resolve)
// 如下是父組件的 render 方法的異步組件加載器的 JSX 標籤
<AsyncLoad modules={Leaner} displayComponent={this.state.displayComponent}></AsyncLoad>
asyncLoader.js 異步組件加載器中的代碼
具體解釋請看代碼註釋
/**
* Created by 王冬 on 2018/2/8.
* QQ: 20004604
* weChat: qq20004604
* 異步加載工廠組件
*/
import React from "react";
const loadingStatus = {
notLoaded: 0,
loading: 1,
loaded: 2
}
export default class AsyncLoader extends React.Component {
constructor() {
super()
this.state = {
amount: loadingStatus.notLoaded, // 0 表示未加載,1表示加載中,2表示加載完畢。沒有考慮加載失敗的問題(並不難)
displayComponent: false, // 是否顯示組件
component: null // 異步組件被賦值給這個變量
}
}
// 生命週期函數,父組件更改 state 後會觸發這個函數
componentWillReceiveProps(nextProps) {
// 若是沒有modules,則直接報錯
if (!nextProps.modules) {
return console.error('你沒有傳值 modules 給【異步組件加載器】')
}
// 若是 control 值爲 true,且以前未加載過組件(用 amount === 0 來表示)
if (nextProps.displayComponent && this.state.amount === 0) {
console.log('開始加載組件')
// 那麼加載組件
this.setState({
amount: loadingStatus.loading // 表示加載中
})
nextProps.modules(module => {
if (!module.default) {
return console.error('你可能加載多個異步組件,或者加載的組件並不是 React 的組件')
}
// 將異步賦值給 state 相應的變量
console.log('組件加載完畢')
this.setState({
amount: loadingStatus.loaded, // 加載完畢
component: module.default
})
})
}
this.setState({
displayComponent: nextProps.displayComponent
})
}
render() { /* <React.Fragment> 是 React 的包裹容器(相似 Vue 的 <template> 標籤) */ return <React.Fragment> {/* 只有當前顯示組件,而且組件加載完畢了,才顯示該組件 */} { this.state.displayComponent && this.state.component ? <this.state.component></this.state.component> : null } </React.Fragment> }}--------------------- 做者:qq20004604 來源:CSDN 原文:https://blog.csdn.net/qq20004604/article/details/79318253 版權聲明:本文爲博主原創文章,轉載請附上博文連接!