React router动态加载组件之适配器模式的应用详解


Posted in Javascript onSeptember 12, 2018

前言

本文讲述怎么实现动态加载组件,并借此阐述适配器模式。

一、普通路由例子

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(),并将异步加载的组件,set入state,触发组件重新渲染。

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也是按这种思路去实现的,只不过增加了很多附属的功能点而已。

以上所述是小编给大家介绍的React router动态加载组件之适配器模式的应用,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Javascript 生成指定范围数值随机数
Jan 09 Javascript
jQuery 表单验证扩展(三)
Oct 20 Javascript
JS数学函数Exp使用说明
Aug 09 Javascript
jquery ui对话框实例代码
May 10 Javascript
jquery提取元素里的纯文本不包含span等里的内容
Sep 30 Javascript
jQuery $命名冲突解决方案汇总
Nov 13 Javascript
JS+CSS实现淡入式焦点图片幻灯切换效果的方法
Feb 26 Javascript
jQuery的层级查找方式分析
Jun 16 Javascript
VUE2.0中Jsonp的使用方法
May 22 Javascript
详解angular如何调用HTML字符串的方法
Jun 30 Javascript
深入理解react 组件类型及使用场景
Mar 07 Javascript
全局安装 Vue cli3 和 继续使用 Vue-cli2.x操作
Sep 08 Javascript
微信小程序实现tab左右切换效果
Nov 15 #Javascript
微信小程序首页的分类功能和搜索功能的实现思路及代码详解
Sep 11 #Javascript
vue项目中使用tinymce编辑器的步骤详解
Sep 11 #Javascript
VUE 实现滚动监听 导航栏置顶的方法
Sep 11 #Javascript
vue中的watch监听数据变化及watch中各属性的详解
Sep 11 #Javascript
vue axios数据请求get、post方法及实例详解
Sep 11 #Javascript
js监听html页面的上下滚动事件方法
Sep 11 #Javascript
You might like
PHP中的日期处理方法集锦
2007/01/02 PHP
PHP 之Section与Cookie使用总结
2012/09/14 PHP
PHP获取当前日期及本周一是几月几号的方法
2017/03/28 PHP
javascript 日期常用的方法
2009/11/11 Javascript
ExtJs使用IFrame的实现代码
2010/03/24 Javascript
jquery键盘事件介绍
2011/01/31 Javascript
网页加载时页面显示进度条加载完成之后显示网页内容
2012/12/23 Javascript
JS 加入收藏夹的代码(主流浏览器通用)
2013/05/13 Javascript
获取数组中最大最小值方法js代码(自写)
2013/08/12 Javascript
jquery实现图片左右切换的方法
2015/05/07 Javascript
更高效的使用JQuery 这里总结了8个小技巧
2016/04/13 Javascript
JS和JQuery实现雪花飘落效果
2017/11/30 jQuery
webpack配置导致字体图标无法显示的解决方法
2018/03/06 Javascript
深入浅析AngularJs模版与v-bind
2018/07/06 Javascript
小程序如何使用分包加载的实现方法
2019/05/22 Javascript
Vue.js实现tab切换效果
2019/07/24 Javascript
JS跨浏览器解析XML应用过程详解
2020/10/16 Javascript
浅析Python中的join()方法的使用
2015/05/19 Python
Python中矩阵库Numpy基本操作详解
2017/11/21 Python
python对日志进行处理的实例代码
2018/10/06 Python
通过PHP与Python代码对比的语法差异详解
2019/07/10 Python
opencv resize图片为正方形尺寸的实现方法
2019/12/26 Python
Django视图、传参和forms验证操作
2020/07/15 Python
Python 读取位于包中的数据文件
2020/08/07 Python
python BeautifulSoup库的安装与使用
2020/12/17 Python
浅谈HTML5 defer和async的区别
2016/06/07 HTML / CSS
Vero Moda西班牙官方购物网站:丹麦BESTSELLER旗下知名女装品牌
2018/04/27 全球购物
20世纪40年代连衣裙和复古服装:The Seamstress Of Bloomsbury
2018/07/24 全球购物
奢华的意大利皮革手袋:Bene Handbags
2019/10/29 全球购物
教师先进工作者事迹材料
2014/05/01 职场文书
大班上学期个人总结
2015/02/13 职场文书
百日宴上的祝酒词
2015/08/10 职场文书
爱岗敬业事迹材料
2019/06/20 职场文书
日本十大血腥动漫,那些被禁播的动漫盘点
2022/03/21 日漫
Python使用华为API为图像设置多个锚点标签
2022/04/12 Python
Promise静态四兄弟实现示例详解
2022/07/07 Javascript