[译]Reactjs性能篇

英文有限,技术一般,海涵海涵,由于不是翻译出身,所以存在大量的瞎胡乱翻译的情况,信不过我的,请看原文~~

原文地址:https://facebook.github.io/react/docs/advanced-performance.html


###性能优化

每当开发者选择将react用在真实项目中时都会先问一个问题:使用react是否会让项目速度更快,更灵活,更容易维护。此外每次状态数据发生改变时都会进行重新渲染界面的处理做法会不会造成性能瓶颈?而在react内部则是通过使用一些精妙的技巧来最小化每次造成ui更新的昂贵的dom操作从而保证性能的。

####避免直接作用于DOM
react实现了一层虚拟dom,它用来映射浏览器的原生dom树。通过这一层虚拟的dom,可以让react避免直接操作dom,因为直接操作浏览器dom的速度要远低于操作javascript对象。每当组件的属性或者状态发生改变时,react会在内存中构造一个新的虚拟dom与原先老的进行对比,用来判断是否需要更新浏览器的dom树,这样就尽可能的优化了渲染dom的性能损耗。

在此之上,react提供了组件生命周期函数,shouldComponentUpdate,组件在决定重新渲染(虚拟dom比对完毕生成最终的dom后)之前会调用该函数,该函数将是否重新渲染的权限交给了开发者,该函数默认直接返回true,表示默认直接出发dom更新:

shouldComponentUpdate: function(nextProps, nextState) {return true;
}

值得注意的是,react会非常频繁的调用该函数,所以如果你打算自己实现该函数的逻辑,请尽可能保证性能。

比方说,你有一个拥有多个帖子的聊天应用,如果此时只有一个发生了变化,如果你如下实现了shouldComponentUpdate,react会根据情况避免重新渲染那些没有发生变化的帖子:

shouldComponentUpdate: function(nextProps, nextState) {// TODO: return whether or not current chat thread is different to former one.// 根据实际情况判断当前帖子的状态是否和之前不同
}

总之,react尽可能的避免了昂贵的dom操作,并且允许开发者干涉该行为。

####shouldComponentUpdate实战
这里举个包含子元素的组件例子,如下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图中每个圆点表示一个dom节点,当某个dom节点的shouldComponentUpdate返回false时(例如c2),react就无需为其更新dom,注意,react甚至根本不会去调用c4和c5节点的shouldComponentUpdate函数哦~

图中c1和c3的shouldComponentUpdate返回了true,因此react会检查检查其它们包含的直接子节点。最有趣的是c8节点,虽然调用它的shouldComponentUpdate方法返回的是true,但react检查后发现其dom结构并未发生改变,所以react最终是不会重新渲染其浏览器dom的。

上图的情况下,react最终只会重新渲染c6,原因你应该懂的。

那么我们应该如何实现shouldComponentUpdate函数呢?假设你有一个只包含字符串的组件,如下:

React.createClass({propTypes: {value: React.PropTypes.string.isRequired},render: function() {return <div>{this.props.value}</div>;}
});

我们可以简单的直接实现shouldComponentUpdate如下:

shouldComponentUpdate: function(nextProps, nextState) {return this.props.value !== nextProps.value;
}

目前为止一切都很顺利,处理基础类型的属性和状态是很简单的,我们可以直接使用js语言提供的===比对来实现一个mix并注入到所有组件中,事实上,react自身已经提供了一个类似的:PureRenderMixin。

但是如果你的组件所拥有的属性或状态不是基础类型呢,而是复合类型呢?比方说是一个js对象,{foo: 'bar'}

React.createClass({propTypes: {value: React.PropTypes.object.isRequired},render: function() {return <div>{this.props.value.foo}</div>;}
});

这种情况下我们刚才实现的那种shouldComponentUpdate就歇菜了:

// 假设 this.props.value 是 { foo: 'bar' }
// 假设 nextProps.value 是 { foo: 'bar' },
// 但是nextProps和this.props对应的引用不相同
this.props.value !== nextProps.value; // true

要想修复这个问题,简单粗暴的方法是我们直接比对foo的值,如下:

shouldComponentUpdate: function(nextProps, nextState) {return this.props.value.foo !== nextProps.value.foo;
}

我们当然可以通过深比对来确定属性或状态是否确实发生了改变,但是这种深比对是非常昂贵的,还记得我们刚出说过shouldComponentUpdate函数的调用非常频繁么?更何况我们为每个model去单独实现一个匹配的深比对逻辑,对于开发人员来说也是非常痛苦的。最重要的是,如果我们不是很小心的处理对象引用关系的话,还会带来灾难。例如下面这个组件:

React.createClass({getInitialState: function() {return { value: { foo: 'bar' } };},onClick: function() {var value = this.state.value;value.foo += 'bar'; // ANTI-PATTERN!this.setState({ value: value });},render: function() {return (<div><InnerComponent value={this.state.value} /><a onClick={this.onClick}>Click me</a></div>);}
});

起初,InnerComponent组件进行渲染,它得到的value属性为{foo: 'bar'}。当用户点击链接后,父组件的状态将会更新为{ value: { foo: 'barbar' } },触发了InnerComponent组件的重新渲染,因为它得到了一个新的属性:{ foo: 'barbar' }

看上去一切都挺好的,其实问题在于,父组件和子组件供用了同一个对象的引用,当用户触发click事件时,InnerComponent的prop将会发生改变,因此它的shouldComponentUpdate函数将会被调用,而此时如果按照我们目前的shouldComponentUpdate比对逻辑的话,this.props.value.foonextProps.value.foo是相等的,因为事实上,它们同时引用同一个对象哦~所以,我们将会看到,InnerComponent的ui并没有更新。哎~,不信的话,我贴出完整代码:

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>demo</title><!--引入React库--><script src="lib/react.min.js"></script><!--引入JSX转换库--><script src="lib/JSXTransformer.js"></script><!--组件样式--></head><body><!--定义容器--><div id="content"></div><!--声明脚本类型为JSX--><script type="text/jsx">var InnerComponent = React.createClass({shouldComponentUpdate: function(nextProps, nextState) {return this.props.value.foo !== nextProps.value.foo;},render: function() {return (<div>{this.props.value.foo}</div>);}});var OutComponent = React.createClass({getInitialState: function() {return { value: { foo: 'bar' } };},onClick: function() {var value = this.state.value;value.foo += 'bar'; // ANTI-PATTERN!this.setState({ value: value });},render: function() {return (<div><InnerComponent value={this.state.value} /><a onClick={this.onClick}>Click me</a></div>);}});React.render(<OutComponent />, document.querySelector("#content"));</script></body>
</html>

####Immutable-js的救赎
Immutable-js是一个javascript集合库,作者是Lee Byron,该项目最近从fb开源。它提供了不可变集合类型:

  • 不可变性:一旦创建,这个集合不允许再更改。
  • 延续性:新集合可以衍生自一个已经创建过的集合,并作一些改动,此时源集合不会受到任何影响。
  • 结构共享:如果新集合衍生自一个老集合,那么新集合中与老集合相同的部份将会共享同一块内存,这样做的好处是节省内存开销,并能在创建新集合对象时减少内存拷贝的性能损耗。

不可变性使得监控状态变化变得可行,每次状态发生变化,将总是返回一个新的对象,我们只需要检查改变前后的对象引用是否相同即可。举个例子:

var x = { foo: "bar" };
var y = x;
y.foo = "baz";
x === y; // true

尽管变量y被修改,但由于y和x的引用相同,最后的比对仍然返回true。如果上面的代码用immutable-js来实现:

var SomeRecord = Immutable.Record({ foo: null });
var x = new SomeRecord({ foo: 'bar'  });
var y = x.set('foo', 'baz');
x === y; // false

看到了么,是不是很爽?

另外一种监控变量的方法是设置一个标识位,但这需要开发者编写额外的代码,哎,反正活着就是麻烦。

总之,不可变集合结构提供了你一个廉价且简单的监控对象改变的方法,你可以放在shouldComponentUpdate中。因此,如果你的model属性和状态是基于immutable-js来实现的,那么你就可以直接使用官方提供的PureRenderMixin哟~

####Immutable-js和Flux
如果你恰巧使用Flux,并且你又基于immutable-js来实现你的store,那你先看一下相关的api吧。

让我们来用个模拟应用演示一下使用一种可行的方案。首先,我们需要定义一下用到的model:

var User = Immutable.Record({id: undefined,name: undefined,email: undefined
});var Message = Immutable.Record({timestamp: new Date(),sender: undefined,text: ''
});

每个Record接受一个对象,分别定义了字段和默认值。我们的messages store需要使用的是list结构:

this.users = Immutable.List();
this.messages = Immutable.List();

很简单吧,接着,每当store接受到一个新的message时,我们只需要创建一个新的record并把它加入到列表中即可:

this.messages = this.messages.push(new Message({timestamp: payload.timestamp,sender: payload.sender,text: payload.text
});

对比我们刚刚讲过的immutable-js特性,在react的组件中我们只需要直接注入PureRenderMixin即可高枕无忧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/39923.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaSE阶段面试题(一)

目录 1.int a 1, int b 1, Integer c 1, Integer d 1&#xff1b;四个区别和联系&#xff0c;以及c和d是同一个吗&#xff1f; 2.为什么重写HashCode必须重写euqals&#xff0c;两者之间的关系&#xff1f; 3.创建对象的方式有哪些 4.重写和重载的区别 5.抽象类和接口…

day02-广播机制

广播机制 广播是numpy对不同形状的数组进行数值计算的方式&#xff0c;对数组的算术运算通常在相应的元素上进行 1.如果两个数组a和b形状相同&#xff0c;即满足a.shape b.shape&#xff0c;那么a*b的结果就是a与b数组对应位相乘。这要求维数相同且各维度的长度相同 a np.a…

七大排序算法的深入浅出(java篇)

&#x1f341; 个人主页&#xff1a;爱编程的Tom&#x1f4ab; 本篇博文收录专栏&#xff1a;Java专栏&#x1f449; 目前其它专栏&#xff1a;c系列小游戏 c语言系列--万物的开始_ 等等 &#x1f389; 欢迎 &#x1f44d;点赞✍评论⭐收藏&#x1f496;三连支…

【高级篇】第9章 Elasticsearch 监控与故障排查

9.1 引言 在现代数据驱动的应用架构中,Elasticsearch不仅是海量数据索引和搜索的核心,其稳定性和性能直接影响到整个业务链路的健康度。因此,建立有效的监控体系和掌握故障排查技能是每一位Elasticsearch高级专家的必备能力。 9.2 监控工具:洞察与优化的利器 在Elastics…

乘用车副水箱浮球式液位计传感器

浮球式液位计概述 浮球式液位计是一种利用浮球在液体中浮动的原理来测量液位的设备&#xff0c;广泛应用于各种工业自动化控制系统中&#xff0c;如石油化工、水处理、食品饮料等行业。它通过浮球的上下运动来测量液位的高低&#xff0c;具有结构简单、安装方便、测量范围广、…

如何选择适合自己的虚拟化技术?

虚拟化技术已成为现代数据中心和云计算环境的核心组成部分。本文将帮助您了解如何选择适合自己需求的虚拟化技术&#xff0c;以实现更高的效率、资源利用率和灵活性。 理解虚拟化技术 首先&#xff0c;让我们了解虚拟化技术的基本概念。虚拟化允许将一个物理服务器划分为多个虚…

【Linux】多线程(一万六千字)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 文章目录 前言 线程的概念 线程的理解(Linux系统为例) 在Linux系统里如何保证让正文部分的代码可以并发的去跑呢&#xff1f; 为什么要有多进程呢&#xff1f; 为…

Osg中的智能指针和观察指针

目录 1 设计 内容 1 设计 osg中能够使用智能指针的对象都继承自引用计数类Referenced&#xff0c;观察指针(observer_ptr)与智能指针之间通过ObserverSet相互关联&#xff0c;其中obserserver_ptr直接依赖ObeserverSet。 Referenced不直接依赖ObserverSet类&#xff0c;但可…

pdf合并,pdf合并成一个pdf,pdf合并在线网页版

在处理pdf文件的过程中&#xff0c;有时我们需要将多个pdf文件合并成一个pdf文件。作为一名有着丰富计算机应用经验的技术博主&#xff0c;我将为您详细介绍如何将多个pdf文件合并成一个pdf文件。 pdf合并方法&#xff1a;使用&#xff0c; “轻云处理pdf官网” 打开 “轻云处…

【高中数学/基本不等式】已知:x,y皆大于1,且x+2y=4 求:1/(x-1)+1/(y-1)的最小值为?

【问题来源】 https://www.ixigua.com/7025123539728466469?logTag1c2fd2e305d60e6277ab 之第一题 【问题】 已知&#xff1a;x,y皆大于1&#xff0c;且x2y4 求&#xff1a;1/(x-1)1/(y-1)的最小值为&#xff1f; 【解答】 解&#xff1a; 若将(x2y)/41代入目标式&…

学习笔记(linux高级编程)11

进程间通信 》信号通信 应用&#xff1a;异步通信。 中断&#xff0c;&#xff0c; 1~64&#xff1b;32应用编程。 如何响应&#xff1a; Term Default action is to terminate the process. Ign Default action is to ignore the signal. wait Core Default action is …

番外篇 | 斯坦福提出即插即用二阶优化器Sophia :相比Adam实现2倍加速,显著节省大语言模型训练成本

前言:Hello大家好,我是小哥谈。大模型的预训练成本巨大,优化算法的改进可以加快模型的训练时间并减少训练开销。目前大模型的训练优化器基本上都采用Adam及其变体,并且Adam的应用已经有9个年头了,在模型优化方面相当于霸主的地位。但是能否够在优化器方面提高模型预训练效…

医院挂号系统:基于JSP和MySQL的现代化医疗预约平台

开头语&#xff1a;您好&#xff0c;我是专注于医疗系统开发的IT学长。如果您对医院挂号系统感兴趣&#xff0c;欢迎联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术&#xff0c;B/S架构 工具&#xff1a;Eclipse&#xff0c;MyEclips…

中国企业消费管理,驶入“降本”大潮

在足够明晰的成本优化方案之上&#xff0c;能看到除了美团企业版的产品和需求理解能力&#xff0c;更本质的恰在于其最核心的原子能力——即强供应链底座。 这种兼备深度和广度的能力的最终呈现也就是如今美团为企业提供的多元、优质、满足需求的成本考校和计算方案。 作者…

【Python】成功解决TypeError: ‘float‘ object cannot be interpreted as an integer

【Python】成功解决TypeError: ‘float’ object cannot be interpreted as an integer 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主…

客户端渗透

1.一键可执行程序 2.给程序加壳 3.宏病毒感染文档 4.Android apk 利用 1.一键可执行程序 介绍&#xff1a;我们要进行客户端渗透&#xff0c;我们生成一个可执行程序&#xff0c;也是简单粗暴&#xff0c;MSF建立监听&#xff0c;把它发给受害者&#xff0c;只要受害者点击…

VQ-VAE中如何解决梯度截断(不可导)问题?直通估计、(stop gradient,停止梯度)

&#xff08;stop gradient&#xff0c;停止梯度&#xff09;运算 它简称为sg 也就是说&#xff0c;前向传播时&#xff0c;&#x1d460;&#x1d454;里的值不变(sg1)&#xff1b;反向传播时&#xff0c;&#x1d460;&#x1d454;按值为0求导(sg0)&#xff0c;即此次计算无…

Qt Q_ASSERT详解

Q_ASSERT详解 引言一、基本用法二、深入了解三、参考链接 引言 Q_ASSERT是 Qt 框架中的一个宏&#xff0c;用于在调试时检查某个条件是否为真。它是程序调试中的一个重要工具&#xff0c;有助于开发者在开发过程中及时发现并修复潜在的错误。 一、基本用法 只在使用 Qt 的 D…

干货:科技论文写作保姆级攻略

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。科技论文是报道自然科学研究或技术开发工作成果的论说文章。通常基于概念、判断、推理、证明或反驳等逻辑思维体系&#xff0c;使用实验调研或理论计算等研究手段&#xff0c;按照特定格式撰写完成。 科技论文可以粗略分为…

UiPath+Appium实现app自动化测试

一、环境准备工作 1.1 完成appium环境的搭建 参考&#xff1a;pythonappiumpytestallure模拟器(MuMu)自动化测试环境搭建_appium mumu模拟器-CSDN博客 1.2 完成uipath的安装 登录官网&#xff0c;完成注册与软件下载安装。 UiPath业务自动化平台&#xff1a;先进的RPA及自动…