React router動態(tài)加載組件之適配器模式的應(yīng)用詳解
前言
本文講述怎么實現(xiàn)動態(tài)加載組件,并借此闡述適配器模式。
一、普通路由例子
import Center from 'page/center'; import Data from 'page/data'; function App(){ return ( <Router> <Switch> <Route exact path="/" render={() => (<Redirect to="/center" />)} /> <Route path="/data" component={Data} /> <Route path="/center" component={Center} /> <Route render={() => <h1 style={{ textAlign: 'center', marginTop: '160px', color:'rgba(255, 255, 255, 0.7)' }}>頁面不見了</h1>} /> </Switch> </Router> ); }
以上是最常見的React router。在簡單的單頁應(yīng)用中,這樣寫是ok的。因為打包后的單一js文件bundle.js也不過200k左右,gzip之后,對加載性能并沒有太大的影響。
但是,當(dāng)產(chǎn)品經(jīng)歷多次迭代后,追加的頁面導(dǎo)致bundle.js的體積不斷變大。這時候,優(yōu)化就變得很有必要。
二、如何優(yōu)化
優(yōu)化使用到的一個重要理念就是——按需加載。
可以結(jié)合例子進(jìn)行理解為:只加載當(dāng)前頁面需要用到的組件。
比如當(dāng)前訪問的是/center頁,那么只需要加載Center組件即可。不需要加載Data組件。
業(yè)界目前實現(xiàn)的方案有以下幾種:
•react-router的動態(tài)路由getComponent方法(router4已不支持)
•使用react-loadable小工具庫
•自定義高階組件進(jìn)行按需加載
而這些方案共通的點,就是利用webpack的code splitting功能(webpack1使用require.ensure,webpack2/webpack3使用import),將代碼進(jìn)行分割。
接下來,將介紹如何用自定義高階組件實現(xiàn)按需加載。
三、自定義高階組件
3.1 webpack的import方法
webpack將import()看做一個分割點并將其請求的module打包為一個獨(dú)立的chunk。import()以模塊名稱作為參數(shù)名并且返回一個Promise對象。
因為import()返回的是Promise對象,所以不能直接給<Router/>使用。
3.2 采用適配器模式封裝import()
適配器模式(Adapter):將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。
當(dāng)前場景,需要解決的是,使用import()異步加載組件后,如何將加載的組件交給React進(jìn)行更新。
方法也很容易,就是利用state。當(dāng)異步加載好組件后,調(diào)用setState方法,就可以通知到。
那么,依照這個思路,新建個高階組件,運(yùn)用適配器模式,來對import()進(jìn)行封裝。
3.3 實現(xiàn)異步加載方法asyncComponent
import React from 'react'; export const asyncComponent = loadComponent => ( class AsyncComponent extends React.Component { constructor(...args){ super(...args); this.state = { Component: null, }; this.hasLoadedComponent = this.hasLoadedComponent.bind(this); } componentWillMount() { if(this.hasLoadedComponent()){ return; } loadComponent() .then(module => module.default ? module.default : module) .then(Component => { this.setState({ Component }); }) .catch(error => { /*eslint-disable*/ console.error('cannot load Component in <AsyncComponent>'); /*eslint-enable*/ throw error; }) } hasLoadedComponent() { return this.state.Component !== null; } render(){ const { Component } = this.state; return (Component) ? <Component {...this.props} /> : null; } } ); // 使用方式 const Center = asyncComponent(()=>import(/* webpackChunkName: 'pageCenter' */'page/center'));
如例子所示,新建一個asyncComponent方法,用于接收import()返回的Promise對象。
當(dāng)componentWillMount時(服務(wù)端渲染也有該生命周期方法),執(zhí)行import(),并將異步加載的組件,set入state,觸發(fā)組件重新渲染。
3.4 釋疑
•state.Component初始化
this.state = { Component: null, };
這里的null,主要用于判斷異步組件是否已經(jīng)加載。
•module.default ? module.default : module
這里是為了兼容具名和default兩種export寫法。
•return (Component) ? <Component {...this.props} /> : null;
這里的null,其實可以用<LoadingComponent />代替。作用是:當(dāng)異步組件還沒加載好時,起到占位的作用。
this.props是通過AsyncComponent組件透傳給異步組件的。
3.5 修改webpack構(gòu)建
output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') }
在輸出項中,增加chunkFilename即可。
四、小結(jié)
自定義高階組件的好處,是可以按最少的改動,來優(yōu)化已有的舊項目。
像上面的例子,只需要改變import組件的方式即可。花最少的代價,就可以得到頁面性能的提升。
其實,react-loadable也是按這種思路去實現(xiàn)的,只不過增加了很多附屬的功能點而已。
以上所述是小編給大家介紹的React router動態(tài)加載組件之適配器模式的應(yīng)用,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
react+zarm實現(xiàn)底部導(dǎo)航欄的示例代碼
本文主要介紹了react?+?zarm?實現(xiàn)底部導(dǎo)航欄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05淺談react-router HashRouter和BrowserRouter的使用
本篇文章主要介紹了淺談react-router HashRouter和BrowserRouter的使用,具有一定的參考價值,有興趣的可以了解一下2017-12-12react16.8.0以上MobX在hook中的使用方法詳解
這篇文章主要為大家介紹了react16.8.0以上MobX在hook中的使用方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07react?hooks深拷貝后無法保留視圖狀態(tài)解決方法
這篇文章主要為大家介紹了react?hooks深拷貝后無法保留視圖狀態(tài)解決示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06React.memo函數(shù)中的參數(shù)示例詳解
這篇文章主要為大家介紹了React.memo函數(shù)中的參數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09