前言
本文講述怎么實現動態加載組件,并借此闡述適配器模式。
一、普通路由例子
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'));            
新聞熱點
疑難解答
圖片精選