React router動態載入組件-適配器模式的應用

來源:互聯網
上載者:User
前言

本文講述怎麼實現動態載入組件,並藉此闡述適配器模式。

一、普通路由例子
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。在簡單的單頁應用中,這樣寫是ok的。因為打包後的單一js檔案bundle.js也不過200k左右,gzip之後,對載入效能並沒有太大的影響。
但是,當產品經曆多次迭代後,追加的頁面導致bundle.js的體積不斷變大。這時候,最佳化就變得很有必要。

二、如何最佳化

最佳化使用到的一個重要理念就是——按需載入
可以結合例子進行理解為:只載入當前頁面需要用到的組件。

比如當前訪問的是/center頁,那麼只需要載入Center組件即可。不需要載入Data組件。

業界目前實現的方案有以下幾種:

  • react-router的動態路由getComponent方法(router4已不支援)
  • 使用react-loadable小工具庫
  • 自訂高階組件進行按需載入

而這些方案共通的點,就是利用webpack的code splitting功能(webpack1使用require.ensure,webpack2/webpack3使用import),將代碼進行分割。

接下來,將介紹如何用自訂高階組件實現按需載入。

三、自訂高階組件3.1 webpack的import方法

webpack將import()看做一個分割點並將其請求的module打包為一個獨立的chunk。import()以模組名稱作為參數名並且返回一個Promise對象

因為import()返回的是Promise對象,所以不能直接給<Router/>使用。

3.2 採用適配器模式封裝import()

適配器模式(Adapter):將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。

當前情境,需要解決的是,使用import()非同步載入組件後,如何將載入的組件交給React進行更新。
方法也很容易,就是利用state。當非同步載入好組件後,調用setState方法,就可以通知到。
那麼,依照這個思路,建立個高階組件,運用適配器模式,來對import()進行封裝。

3.3 實現非同步載入方法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對象。
componentWillMount時(服務端渲染也有該生命週期方法),執行import(),並將非同步載入的組件,setstate,觸發組件重新渲染。

3.4 釋疑
  • state.Component初始化
this.state = {    Component: null,};

這裡的null,主要用於判斷非同步裝置是否已經載入。

  • module.default ? module.default : module

這裡是為了相容具名default兩種export寫法。

  • return (Component) ? <Component {...this.props} /> : null;

這裡的null,其實可以用<LoadingComponent />代替。作用是:當非同步裝置還沒載入好時,起到佔位的作用。
this.props是通過AsyncComponent組件透傳給非同步裝置的。

3.5 修改webpack構建
output: {    path: config.build.assetsRoot,    filename: utils.assetsPath('js/[name].[chunkhash].js'),    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')}

在輸出項中,增加chunkFilename即可。

四、小結

自訂高階組件的好處,是可以按最少的改動,來最佳化已有的舊項目。
像上面的例子,只需要改變import組件的方式即可。花最少的代價,就可以得到頁面效能的提升。
其實,react-loadable也是按這種思路去實現的,只不過增加了很多附屬的功能點而已。

參考
  1. 基於webpack Code Splitting實現react組件的按需載入
  2. react中使用webpack2的import()非同步載入組件的實現
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.