资讯专栏INFORMATION COLUMN

React Render Array 性能大乱斗

wenshi11019 / 420人阅读

摘要:现在关于最新版本新特性的宣传讲解已经铺天盖地了。测试场景是反复操作数组,这个反复操作有所讲究,我们计划持续不断地改变数组的某一项而不是整个数组的大范围变动。代码和性能测试在使用开发时,相信很多开发者在搭配函数式的状态管理框架使用。

现在关于 React 最新 v16 版本新特性的宣传、讲解已经“铺天盖地”了。你最喜欢哪一个 new feature?
截至目前,组件构建方式已经琳琅满目。那么,你考虑过他们的性能对比吗?这篇文章,聚焦其中一个小细节,进行对比,望读者参考的同时,期待大神斧正。

从 React.PureComponent 说起

先上结论:在我们的测试当中,使用 React.PureComponent 能够提升 30% JavaScript 执行效率。测试场景是反复操作数组,这个“反复操作”有所讲究,我们计划持续不断地改变数组的某一项(而不是整个数组的大范围变动)。

线上参考地址: 请点击这里

那么这样的场景,作为开发者有必要研究吗?如果你的应用并不涉及到高频率的更新数组某几项,那么大可不必在意这些性能的微妙差别。但是如果存在一些“实时更新”的场景,比如:

用户输入改变数组(点赞者显示);

轮询(股票实时);

推更新(比赛比分实时播报);

那么就需要进行考虑。我们定义:changedItems.length / array.length 比例越小,本文所涉及的性能优化越应该实施,即越有必要使用 React.PureComponent。

代码和性能测试

在使用 React 开发时,相信很多开发者在搭配函数式的状态管理框架 Redux 使用。Redux reducers 作为纯函数的同时,也要保证 state 的不可变性,在我们的场景中,也就是说在相关 action 被触发时,需要返回一个新的数组。

const users = (state, action) => {
  if (action.type === "CHANGE_USER_1") {
    return [action.payload, ...state.slice(1)]
  }
  return state
}

如上代码,当 CHANGE_USER_1 时,我们对数组的第一项进行更新,使用 slice 方法,不改变原数组的同时返回新的数组。

我们设想所有的 users 数组被 Users 函数式组件渲染:

import User from "./User"
const Users = ({users}) =>
  
{ users.map(user => }

问题的关键在于:users 数组作为 props 出现,当数组中的第 K 项改变时,所有的 组件都会进行 reconciliation 的过程,即使非 K 项并没有发生变化。

这时候,我们可以引入 React.PureComponent,它通过浅对比规避了不必要的更新过程。即使浅对比自身也有计算成本,但是一般情况下这都不值一提。

以上内容其实已经“老生常谈”了,下面直接进入代码和性能测试环节。

我们渲染了一个有 200 项的数组:

const arraySize = 200;
const getUsers = () =>
  Array(arraySize)
    .fill(1)
    .map((_, index) => ({
      name: "John Doe",
      hobby: "Painting",
      age: index === 0 ? Math.random() * 100 : 50
    }));
    

注意在 getUsers 方法中,关于 age 属性我们做了判断,保证每次调用时,getUsers 返回的数组只有第一项的 age 属性不同。
这个数组将会触发 400 次 re-renders 过程,并且每一次只改变数组第一项的一个属性(age):

  const repeats = 400;
  componentDidUpdate() {
    ++this.renderCount;
    this.dt += performance.now() - this.startTime;
    if (this.renderCount % repeats === 0) {
      if (this.componentUnderTestIndex > -1) {
        this.dts[componentsToTest[this.componentUnderTestIndex]] = this.dt;
        console.log(
          "dt",
          componentsToTest[this.componentUnderTestIndex],
          this.dt
        );
      }
      ++this.componentUnderTestIndex;
      this.dt = 0;
      this.componentUnderTest = componentsToTest[this.componentUnderTestIndex];
    }
    if (this.componentUnderTest) {
      setTimeout(() => {
        this.startTime = performance.now();
        this.setState({ users: getUsers() });
      }, 0);
    } else {
      alert(`
        Render Performance ArraySize: ${arraySize} Repeats: ${repeats}
        Functional: ${Math.round(this.dts.Functional)} ms
        PureComponent: ${Math.round(this.dts.PureComponent)} ms
        Component: ${Math.round(this.dts.Component)} ms
      `);
    }
  }

为此,我们采用三种方式设计 组件。

函数式方式
export const Functional = ({ name, age, hobby }) => (
  
{name} {age} {hobby}
);
PureComponent 方式
export class PureComponent extends React.PureComponent {
  render() {
    const { name, age, hobby } = this.props;
    return (
      
{name} {age} {hobby}
); } }
经典 class 方式
export class Component extends React.Component {
  render() {
    const { name, age, hobby } = this.props;
    return (
      
{name} {age} {hobby}
); } }

同时,在不同的浏览器环境下,我得出:

Firefox 下,PureComponent 收益 30%;

Safari 下,PureComponent 收益 6%;

Chrome 下,PureComponent 收益 15%;

测试硬件环境:

最终结果:

最后,送给大家鲁迅先生的一句话:

“Early optimization is the root of all evil” - 鲁迅

Happy Coding!
PS: 作者 Github仓库 和 知乎问答链接 欢迎各种形式交流。

我的其他几篇关于React技术栈的文章:
React Redux 中间件思想遇见 Web Worker 的灵感(附demo)
了解 Twitter 前端架构 学习复杂场景数据设计
React 探秘 - React Component 和 Element(文末附彩蛋demo和源码)
从setState promise化的探讨 体会React团队设计思想
通过实例,学习编写 React 组件的“最佳实践”
React 组件设计和分解思考
从 React 绑定 this,看 JS 语言发展和框架设计
React 服务端渲染如此轻松 从零开始构建前后端应用
做出Uber移动网页版还不够 极致性能打造才见真章**
React+Redux打造“NEWS EARLY”单页应用 一个项目理解最前沿技术栈真谛**

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89329.html

相关文章

  • 2017-10-27 前端日报

    摘要:前端日报精选装饰器场景实战配置之后端渲染理解同步异步和事件循环编写高性能注意点线性渐变实现虚线等简单实用图形中文服务端渲染开发指南个人文章系列之事件类型个人文章使用必记掘金简介掘金性能大乱斗前端杂谈中简单的数据图形化 2017-10-27 前端日报 精选 JS 装饰器(Decorator)场景实战webpack配置之后端渲染JavaScript:理解同步、异步和事件循环编写高性能js注...

    Jeffrrey 评论0 收藏0
  • 像素,css像素,物理像素,设备独立像素,分辨率大乱

    摘要:我们用小米举例,屏幕像素物理像素为,设备独立像素为,也就是说,一个设备独立像素就包含个物理像素,同时我们能得出。 本文主要阐述移动端布局中常遇到的一些基本概念,这些概念也适用于PC端,这些概念大概有:像素(pixel),ppi,分辨率,物理像素(physical pixel), CSS像素,设备独立像素(devicedependent pixel) 像素 像素是图像显示的基本单位,同时...

    LiveVideoStack 评论0 收藏0
  • 每日 30 秒 ⏱ 数据类型大乱

    showImg(https://segmentfault.com/img/remote/1460000018796041?w=900&h=500); 简介 字符串、数字、布尔值、Null、Undefined、对象、数组、函数、判断方法 JavaScript 中有两种数据类型,分别是基本数据类型和引用数据类型: 基本数据类型 引用数据类型 Number、String、Boolean、Null...

    meteor199 评论0 收藏0
  • 2017文章总结

    摘要:欢迎来我的个人站点性能优化其他优化浏览器关键渲染路径开启性能优化之旅高性能滚动及页面渲染优化理论写法对压缩率的影响唯快不破应用的个优化步骤进阶鹅厂大神用直出实现网页瞬开缓存网页性能管理详解写给后端程序员的缓存原理介绍年底补课缓存机制优化动 欢迎来我的个人站点 性能优化 其他 优化浏览器关键渲染路径 - 开启性能优化之旅 高性能滚动 scroll 及页面渲染优化 理论 | HTML写法...

    dailybird 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<