当前位置: 首页 > news >正文

利用React 16.6新特性优化应用性能

利用React v16.6 Lazy&Suspense提升应用性能

本篇文章示例代码

github.com/Gavin1995/r…

前言

利用懒加载(Lazy Loading)优化页面性能不是什么新概念,不过React 16.6可以使用React.lazySuspense让原生React实现Lazy Loading大大的简化。

本篇文章也会拓展多种类似解决方案,见拓展部分

主要关键词说明

动态导入(Dynamic Import)

动态导入目前只是TC39的一个提案,不是JS(ES)标准的一部分。该功能可以动态化加载我们分割的组件/页面/文件,具体提案见下方链接: TC39 proposal-dynamic-import提案

目前要使用动态导入,需要使用到babel插件:

babel-plugin-syntax-dynamic-import

React.lazy

lazy函数提供了一种非常简便的方法动态导入组件,实现按需加载与代码分割

点击查看文档

React.lazy使用方式非常简单,示例:

// lazy 接受一个函数作为参数
const MyComponent = React.lazy(() => import('path/MyComponent'));

// 展示时才加载(可以自定义一些条件)
const App = () => (
  <div>
    <MyComponent />
  </div>
)
复制代码

Suspense

Suspense组件用于包装lazy组件,在lazy组件还没有完全加载时,将fallback内容呈现给用户。

用动态加载,编译时会将文件分割,从加载文件到呈现会有时间延迟,此时可以使用Suspense展示一个loading。

点击查看文档

使用示例

import React, { Suspense } from 'react';

const MyComponent = React.lazy(() => import('path/MyComponent'));
const Loading = (
  <h3>loading...</h3>
);

const App = () => (
  <div>
    <Suspense fallback={Loading}>
      <MyComponent />
    </Suspense>
  </div>
)
复制代码

ErrorBoundary

ErrorBoundary(错误边界),可以使用ErrorBoundary包装Suspense,当Suspense出错时,统一处理错误。当然ErrorBoundary不止可用于Suspense,可以包装任意组件,然后统一处理错误。

实际场景中,可以自己先写一些通用错误页/组件继承自ErrorBoundary,然后包装要处理的组件。

也可以在组件的componentDidCatch生命周期中单独处理错误。

点击查看文档

应用

性能优化说明

针对React加载的优化,可以分为两类:

  • 页面加载优化
  • 组件加载优化

很明显,React.lazy + Suspense可以很方便的应用于组件加载优化场景。

示例场景

CSR(客户端渲染)应用多页面的中某个耗时组件的加载

路由层

import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';

import Header from './header';
import Index from '../pages';
import Welcome from '../pages/welcome';

export default () => (
  <Router>
    <div>
      <Header />

      <Route exact path="/" component={Index}/>
      <Route path="/welcome" component={Welcome}/>
    </div>
  </Router>
);
复制代码

某个存在耗时组件的页面

// welcome.js
import React, { Suspense } from 'react';

// 使用Suspense + React.lazy动态加载的耗时组件
const Content = React.lazy(() => import('../components/content'));

export default () => (
  <div>
    <h1>Welcome</h1>
    <Suspense fallback={<div>loading...</div>}>
      <Content/>
    </Suspense>
  </div>
);
复制代码

说明

假设用webpack编译上面代码产出bundle.js。当进入首页时,加载的bundle.js不会包含Welcome Content部分的代码,切换页面到Welcome时会走网络加载需要的文件。

利用这种方法拆分多个组件时,可以显著的提升首屏时间。

完整示例项目

React-New-Features

说明:该项目主要用于React一些新功能及有意思的用法示例,会持续更新,后面会涉及到hooks及最新源码相关,有兴趣的可以star。

拓展

针对SSR(服务端渲染)应用导入性能优化

可以使用 Next.js ,Next.js已支持直接使用dynamic import,并且可以指明什么时候动态导入(服务端/客户端),Next.js对首屏做了很多优化,首屏时,服务端会寻找到最简依赖渲染,具体可以点击前面链接查看文档。示例:

// Next.js 动态导入
const MyComponent = dynamic(import('../components/MyComponent'), {
  ssr: false,
  loading: () => false,
});
复制代码

针对组件的动态加载

  • react-lazyload

针对图片的lazy loading

  • img2
  • react-progressive-image

针对动态请求

  • react-request:对于CSR应用,在不用hooks的情况下,也可以不在componentDidMount中处理请求,让请求更加的优雅。
  • react-async:利用Render Props + React hooks + Context API统一处理请求,支持SSR使用

封装react-request示例(统一请求处理)

import React from 'react';
import PropTypes from 'prop-types';
import { Fetch } from 'react-request';
// import ReactLoading from 'react-loading';

import config from '../../config';

const CustomFetch = (props) => {
  const { url, children } = props;
  return (
    <Fetch
      {...props}
      url={config.serverUrl + url}
      transformData={data => data.data}
    >
      {({ fetching, failed, data }) => {
        if (fetching) {
          // 正在请求:可以展示loading动画
          // return <ReactLoading type="cylon" color="#2db53f" height="10%" width="10%" />;
        }

        if (failed) {
          // 当前组件请求失败
          // return <h1 className="home">请求失败...</h1>;
        }

        if (data) {
          // 请求成功
          return children(data);
        }

        return null;
      }}
    </Fetch>
  );
};

CustomFetch.propTypes = {
  url: PropTypes.string.isRequired,
};

export default CustomFetch;
复制代码

在组件中使用CustomFetch

  buildLinks = () => (
    <div className="links">
      <CustomFetch
        url="/getLinks"
      >
        {data => data.map((item, index) => (
          <a className="item" href={item.url} key={`link-${index}`}>
            <img src={item.icon} className="icon" alt={item.name} />
            <p>{item.name}</p>
          </a>
        ))}
      </CustomFetch>
    </div>
  );
复制代码

后记

其它性能优化方式,以及自己如何实现,可以参考我的上篇文章:大前端性能总结

转载于:https://juejin.im/post/5c32d40951882524b333994d

相关文章:

  • ajax的19道经典面试题
  • 大数据就业前景,分析的太到位了
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • 程序员的本质
  • matlab练习程序(直方图匹配)
  • TortoiseSvn介绍
  • C#中Monitor和Lock以及区别
  • HDU 1575 Tr A (矩阵乘法)
  • MVC使用基架添加控制器出现的错误:无法检索XXX的元数据
  • 从程序员的视角揭秘Silverlight
  • oracle索引再论
  • Android应用开发学习笔记之多线程与Handler消息处理机制
  • jquery $.each() 小探
  • HDU 4089 Activation
  • Linux 上安装 Subversion
  • ----------
  • CentOS从零开始部署Nodejs项目
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JAVA多线程机制解析-volatilesynchronized
  • k个最大的数及变种小结
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 解析带emoji和链接的聊天系统消息
  • 排序算法学习笔记
  • 人脸识别最新开发经验demo
  • 软件开发学习的5大技巧,你知道吗?
  • 数组的操作
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 学习JavaScript数据结构与算法 — 树
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • 交换综合实验一
  • #ifdef 的技巧用法
  • (13):Silverlight 2 数据与通信之WebRequest
  • (2.2w字)前端单元测试之Jest详解篇
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (windows2012共享文件夹和防火墙设置
  • (转)德国人的记事本
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • **CI中自动类加载的用法总结
  • .axf 转化 .bin文件 的方法
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全
  • [ vulhub漏洞复现篇 ] ECShop 2.x / 3.x SQL注入/远程执行代码漏洞 xianzhi-2017-02-82239600
  • [1204 寻找子串位置] 解题报告
  • [android] 切换界面的通用处理
  • [BZOJ4010]菜肴制作
  • [C语言]一维数组二维数组的大小
  • [element-ui] el-dialog 中的内容没有预先加载,因此无法获得内部元素的ref 的解决方案
  • [hdu 2896] 病毒侵袭 [ac自动机][病毒特征码匹配]
  • [Hive] CTE 通用表达式 WITH关键字
  • [JS设计模式]Prototype Pattern
  • [leetcode] 66. 加一
  • [Linux_IMX6ULL应用开发]-Makefile