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 相关文章推荐
jquery选择器-根据多个属性选择示例代码
Oct 21 Javascript
jquery为页面增加快捷键示例
Jan 31 Javascript
javascript学习笔记(五)原型和原型链详解
Oct 08 Javascript
Bootstrap 3的box-sizing样式导致UEditor控件的图片无法正常缩放的解决方案
Sep 15 Javascript
网站申请不到支付宝接口、微信接口,免接口收款实现方式几种解决办法
Dec 14 Javascript
BootStrap与Select2使用小结
Feb 17 Javascript
vue时间格式化实例代码
Jun 13 Javascript
解决Vue使用mint-ui loadmore实现上拉加载与下拉刷新出现一个页面使用多个上拉加载后冲突问题
Nov 07 Javascript
微信小程序App生命周期详解
Jan 31 Javascript
js循环map 获取所有的key和value的实现代码(json)
May 09 Javascript
微信小程序如何获取地址
Dec 24 Javascript
JavaScript实现页面高亮操作提示和蒙板
Jan 04 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基于面向对象实现的留言本功能实例
2018/04/04 PHP
用javascript编写的第一人称射击游戏
2007/02/25 Javascript
window.js 主要包含了页面的一些操作
2009/12/23 Javascript
使用Firebug对js进行断点调试的图文方法
2011/04/02 Javascript
解决javascript:window.close()在chrome,Firefox下失效的问题
2013/05/07 Javascript
使用GruntJS构建Web程序之安装篇
2014/06/04 Javascript
浅谈JavaScript的闭包函数
2016/12/08 Javascript
Bootstrap CSS组件之分页(pagination)和翻页(pager)
2016/12/17 Javascript
Angular多选、全选、批量选择操作实例代码
2017/03/10 Javascript
jquery 回调操作实例分析【回调成功与回调失败的情况】
2019/09/27 jQuery
vue2.0 获取从http接口中获取数据,组件开发,路由配置方式
2019/11/04 Javascript
Jquery异步上传文件代码实例
2019/11/13 jQuery
nuxt 页面路由配置,主页轮播组件开发操作
2020/11/05 Javascript
[01:20]PWL开团时刻DAY9——听说潮汐没用?
2020/11/10 DOTA
python中引用与复制用法实例分析
2015/06/04 Python
Python发送form-data请求及拼接form-data内容的方法
2016/03/05 Python
Python的collections模块中的OrderedDict有序字典
2016/07/07 Python
Python中二维列表如何获取子区域元素的组成
2017/01/19 Python
Python实现爬虫设置代理IP和伪装成浏览器的方法分享
2018/05/07 Python
Python+OpenCV目标跟踪实现基本的运动检测
2018/07/10 Python
opencv python统计及绘制直方图的方法
2019/01/21 Python
pyenv虚拟环境管理python多版本和软件库的方法
2019/12/26 Python
Python爬虫谷歌Chrome F12抓包过程原理解析
2020/06/04 Python
Python爬虫后获取重定向url的两种方法
2021/01/19 Python
华三通信H3C面试题
2015/05/15 面试题
应届生会计电算化求职信
2013/10/03 职场文书
大学军训自我鉴定
2013/12/15 职场文书
大学学习个人的自我评价
2014/02/18 职场文书
演讲稿的写法
2014/05/19 职场文书
银行求职信
2014/05/31 职场文书
应届生求职信范文
2014/06/30 职场文书
团队会宣传标语
2014/10/09 职场文书
幼儿园大班毕业评语
2014/12/31 职场文书
2015年公务员个人工作总结
2015/04/24 职场文书
你知道Java Spring的两种事务吗
2022/03/16 Java/Android
html解决浏览器记住密码输入框的问题
2023/05/07 HTML / CSS