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

时间: 2018-09-12阅读: 1411标签: 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.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

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

关闭

必须要会的 50 个React 面试题

如果你是一位有抱负的前端程序员并准备面试,那么这篇文章很适合你。本文是你学习和面试 React 所需知识的完美指南。JavaScript 工具缓慢而稳定地在市场中扎根,对 React 的需求呈指数级增长。选择合适的技术来开发应用或网站变得越来越有挑战性

react中PureComponent浅对比策略

PureComponent实现了Component中没有实现的shouComponentUpdata()方法,会对state和props进行一次浅对比,本文介绍一下浅对比策略,源码中,实现浅对比的函数是:shallowEqual()

解读React的pooledClass.js_对象池技术的原理/思路

单例模式是限制了一个类只能有一个实例,对象池模式则是限制一个类实例的个数。对象池技术基本原理的核心有两点:缓存和共享,即对于那些被频繁使用的对象,在使用完后,不立即将它们释放,而是将它们缓存起来

自定义组件v-model的实质性理解

在React中如果想实现类似于v-model的功能,一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目的

react-router v4 按需加载的配置方法

在react项目开发中,当访问默认页面时会一次性请求所有的js资源,这会大大影响页面的加载速度和用户体验。所以添加按需加载功能是必要的,以下是配置按需加载的方法

10分钟快速了解 React 基础

如果你还不会 React,希望本文可以帮你快速了解 React.js 的基础知识。使用 create-react-app 工具快速创建 React SPA。render 函数中提到了 JSX,简单的看,就是一个可以在 js 中写 html

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

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

不要过度使用React.useCallback()

我博客的一位读者在Facebook上联系到我,提出了一个有趣的问题。他说,他的队友不管在什么情况下,都会把每一个回调函数封装在 useCallback() 里面。

react-router 路由切换动画

因为项目的需求,需要在路由切换的时候,加入一些比较 zb 的视觉效果,所以研究了一下。把这些学习的过程记录下来,以便以后回顾。同时也希望这些内容能够帮助一些跟我一样的菜鸟,让他们少走些坑。可能我对代码的表述不是很到位,希望大家不要介意。机智的你们一定可以看明白。

React新Context API在前端状态管理的实践

众所周知,React的单向数据流模式导致状态只能一级一级的由父组件传递到子组件,在大中型应用中较为繁琐不好管理,通常我们需要使用Redux来帮助我们进行管理,然而随着React 16.3的发布,新context api成为了新的选择。

点击更多...

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