关闭

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

时间: 2018-09-12阅读: 1770标签: react

前言

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


一、普通路由例子

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


参考

  1. 基于webpack Code Splitting实现react组件的按需加载
  2. react中使用webpack2的import()异步加载组件的实现

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: http://www.fly63.com/article/detial/1071

为什么我会选择React+Next.js,而不是Vue或Angular?

本文的目的不是要对 React、Vue 和 Angular 三者进行比较,已经有许多人对这个话题进行了比较深入的探讨。每个人都有自己的偏好。与其他库和框架相比,我更喜欢使用 React 构建用户界面。 对我来说,这是构建用户界面唯一正确的方法,它让我爱上了 React。

react生命周期详解_深入理解React生命周期

React主要思想是通过构建可复用组件来构建用户界面,每个组件都有自己的生命周期,它规定了组件的状态和方法需要在哪个阶段改变和执行。所谓组件就是有限状态机,,表示有限个状态以及在这些状态之间的转移和动作行为的模型。

React列表中实现文案多行收起展开的功能

在我们平时的业务开发中经常会用到文案超出只有收起,点击在展示全部文案;通常的使用时使用css来实现

渐进式React

可以说 React 为Web开发者带来了全新的开发模式,而在各类新功能下,如何达到性能最优仍是我们需要关心的。今天做一次精读尝试,原文地址在文末,话不多说,先呈上一份性能清单:

React ref

React ref理解:通过指定ref获得你想操作的元素,然后进行修改.是当组件挂载后和卸载后,以及ref属性本身发生变化时,回调函数就会被调用。 因为ref引用的是组件的实例

React中组件逻辑复用的那些事儿

基本每个开发者都需要考虑逻辑复用的问题,否则你的项目中将充斥着大量的重复代码。那么 React 是怎么复用组件逻辑的呢?本文将一一介绍 React 复用组件逻辑的几种方法,希望你读完之后能够有所收获。如果你对这些内容已经非常清楚,那么略过本文即可。

Context - React跨组件访问数据的利器

Context提供了一种跨组件访问数据的方法。它无需在组件树间逐层传递属性,也可以方便的访问其他组件的数。在经典的React应用中,数据是父组件通过props向子组件传递的

React 新 Context API

React 新 Context API它更符合工程化, 不再是实验性的,现在它是一流的API! 并且它还使用了 RENDER PROP!你在react官网上听说过 context API?那么你为何要使用context?Context的重生

React Server Component 可能并没有那么香

前段时间 React 团队发布了一项用于解决 React 页面在多接口请求下的性能问题的解决方案 React Server Components。当然该方案目前还在草案阶段,官方也只是发了视频和一个示例 demo 来说明这个草案。

react hook超实用的用法和技巧分析

react hook发布也已经有几个月了,相信有部分人已经开始使用了,还有些人在犹豫要不要用,可能更多人安于现状,没有要用的打算,甚至还有很多公司的react版本是15或以下的,迫于升级的难度没有使用。以我个人的观点,要不要使用react hook呢?

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!