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系列(43):设计模式之状态模式详解
Mar 04 Javascript
js实现选中页面文字将其分享到新浪微博
Nov 05 Javascript
基于jQuery实现仿搜狐辩论投票动画代码(附源码下载)
Feb 18 Javascript
jQuery插件Validation快速完成表单验证的方式
Jul 28 Javascript
js中获取键盘事件的简单实现方法
Oct 10 Javascript
w3c编程挑战_初级脚本算法实战篇
Jun 23 Javascript
JavaScript上传文件时不用刷新页面方法总结(推荐)
Aug 15 Javascript
在微信小程序里使用watch和computed的方法
Aug 02 Javascript
说说Vue.js中的functional函数化组件的使用
Feb 12 Javascript
JQuery发送ajax请求时中文乱码问题解决
Nov 14 jQuery
在webstorm中配置less的方法详解
Sep 25 Javascript
Echarts在Taro微信小程序开发中的踩坑记录
Nov 09 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 判断常量,变量和函数是否存在
2009/04/26 PHP
php中sql注入漏洞示例 sql注入漏洞修复
2014/01/24 PHP
PHP中使用json数据格式定义字面量对象的方法
2014/08/20 PHP
php实现兼容2038年后Unix时间戳转换函数
2015/03/18 PHP
php实现保存周期为1天的购物车类
2017/07/07 PHP
PhpSpreadsheet设置单元格常用操作汇总
2020/11/13 PHP
一个tab标签切换效果代码
2009/03/27 Javascript
javascript学习之闭包分析
2010/12/02 Javascript
JavaScript初学者应注意的七个细节详细介绍
2012/12/27 Javascript
html中使用javascript调用本地程序(exe、doc等)实现代码
2013/04/26 Javascript
jQuery UI 实现email输入提示实例
2013/08/15 Javascript
Javascript对象属性方法汇总
2013/11/21 Javascript
JQuery入门基础小实例(1)
2015/09/17 Javascript
Bootstrap表单布局样式源代码
2016/07/04 Javascript
JS实现点击链接切换显示隐藏内容的方法
2017/10/19 Javascript
layui中使用jquery控制radio选中事件的示例代码
2018/08/15 jQuery
微信小程序实现留言板功能
2018/11/02 Javascript
使用layer弹窗提交表单时判断表单是否输入为空的例子
2019/09/26 Javascript
[02:38]DOTA2英雄基础教程 噬魂鬼
2014/01/03 DOTA
[55:44]OG vs NAVI 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
python 解析XML python模块xml.dom解析xml实例代码
2014/02/07 Python
Python3实现发送QQ邮件功能(html)
2017/12/15 Python
HTML5地理定位实例
2014/10/15 HTML / CSS
域名注册、建站工具、网页主机、SSL证书:Dynadot
2017/01/06 全球购物
Clarins娇韵诗英国官网:来自法国的天然护肤品牌
2017/04/18 全球购物
TUMI新加坡官网:国际领先的商旅箱包品牌
2019/01/12 全球购物
碧欧泉法国官网:Biotherm法国
2019/10/23 全球购物
英国男女豪华配饰和礼品网站:Black.co.uk
2020/02/28 全球购物
药物学专业学生的自我评价
2013/10/27 职场文书
如何写好升职自荐信
2014/01/06 职场文书
京剧自荐信
2014/01/26 职场文书
给老婆的婚前保证书
2014/02/01 职场文书
银行进社区活动总结
2014/07/07 职场文书
何玥事迹观后感
2015/06/16 职场文书
2015入党自传格式范文
2015/06/26 职场文书
保外就医申请书范文
2015/08/06 职场文书