前言

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

一、普通路由例子

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()异步加载组件的实现

React router动态加载组件-适配器模式的应用的更多相关文章

  1. vue中动态加载组件+开发者模式+JS参数值传递和引用传递

    今天写vue里面通过接口反参动态加载组件时候 跟着同学...学习到了 一.先说说vue 内置组件 component 的用法 component组件可以来专门用来进行组件的切换,使用is来绑定你的组件 ...

  2. Vue 动态加载组件

    为什么要动态加载呢?而不是一次性加载呢? 一次性?你能保证你拿的内容不多,那从性能方面说还是OK的.否则,就该什么时候用,就什么时候取. 得出这想法,源于前几天上班赶产品的故事: A组件是父亲,B组件 ...

  3. Vue加载组件、动态加载组件的几种方式

    https://cn.vuejs.org/v2/guide/components.html https://cn.vuejs.org/v2/guide/components-dynamic-async ...

  4. C# 动态加载组件后怎么在开发环境中调试

    动态加载组件 那就是简单的Assembly.Load动态加载dll而以.这网上资料也有不少.基本的思路基本上就是在本地上一个指定目录如[plugs]存在着一堆dll文件.主程序在初始运行时一般会把指定 ...

  5. vue动态加载组件

    vue动态加载组件,可以使用以下方式 <component :is="propertyname" v-for="tab in tabs"></ ...

  6. VUE 动态加载组件的四种方式

    动态加载组件的四种方式: 1.使用import导入组件,可以获取到组件 var name = 'system'; var myComponent =() => import('../compon ...

  7. vue实践---vue动态加载组件

    开发中遇到要加载10个或者更多的,类型相同的组件时,如果用普通的 import 引入组件,components注册组件,代码显得太啰嗦了,这时候就需要用到 require.context 动态加载这些 ...

  8. React.lazy和React.Suspense异步加载组件

    在React16.6中引入了React.lazy和React.Suspense,这两个组件,可以用来实现异步加载组件. 例如: const johanComponent = React.lazy(() ...

  9. 实现react路由动态加载的组件

    import React, { Component } from 'react'; import Loading from '../../base/nc_Loading'; /* * date: 20 ...

随机推荐

  1. 如何配置React Native真机调试-iOS

    说在前面,本教程是建立在项目已经成功在模拟器上运行的基础上,如果你是还未配置好环境的新手,建议先从官网快速入门开始:官网英文版 . 中文版 ok, 切入正题,当你已经完成好环境配置,在模拟器上成功的运 ...

  2. Object.defineProperty方法

    Object.defineProperty() (一次添加/修改一个属性) 用法:Object.defineProperty(obj, prop, descriptor) 方法会直接在一个对象上定义一 ...

  3. Linux编程 22 shell编程(输出和输入重定向,管道,数学运算命令,退出脚本状态码)

    1. 输出重定向 最基本的重定向是将命令的输出发送到一个文件中.在bash shell中用大于号(>) ,格式如下:command > inputfile.例如:将date命令的输出内容, ...

  4. MongoDB从入门到优化

    目录 一.MongoDB 简介 二.MongoDB 的储存引擎 三.mongodb 配置参数 四.MongoDB 单节点搭建 五.MongoDB 连接 六.MongoDB 常用命令 七.MongoDB ...

  5. kibana从入门到精通-Kibana安装

    作者其他ELK快速入门系列文章 Elasticsearch从入门到精通 logstash快速入门实战指南 简介 Kibana 是一款开源的数据分析和可视化平台,它是 Elastic Stack 成员之 ...

  6. gops —— Go 程序诊断分析工具

    GitHub: https://github.com/google/gops 一个用于列出和诊断分析系统中正在运行的 Go 程序的命令行工具 安装 go get -u github.com/googl ...

  7. Go标准库:深入剖析Go template

    本文只关注Go text/template的底层结构,带上了很详细的图片以及示例帮助理解,有些地方也附带上了源码进行解释.有了本文的解释,对于Go template的语法以及html/template ...

  8. TensorFlow.js入门(一)一维向量的学习

    TensorFlow的介绍   TensorFlow是谷歌基于DistBelief进行研发的第二代人工智能学习系统,其命名来源于本身的运行原理.Tensor(张量)意味着N维数组,Flow(流)意味着 ...

  9. 跨域学习笔记3--web.config设置之system.webServer 详细介绍,为网站设置默认文档

    自己并不懂,在此先记录下来,留待以后学习... 如何:为 IIS 7.0 配置 <system.webServer> 节2008-06-14 22:26http://technet.mic ...

  10. phpmyadmin创建mysql的存储过程

    通过phpmyadmin ,创建procedure, 用于生成测试数据. 随机的用户名及手机号. DELIMITER $$ CREATE PROCEDURE `sp_insert_test_users ...