[译]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…

Windows和Linux服务器的SSL证书续订方法

在现代互联网中&#xff0c;SSL证书已经成为保障网站安全和数据隐私的必备工具。SSL证书到期后&#xff0c;服务器管理员需要及时续订SSL证书&#xff0c;以确保网站的正常访问和安全性。本文将详细介绍在Windows和Linux服务器上续订SSL证书的步骤。 生成CSR&#xff08;证书签…

CesiumJS【Basic】- #024A mp4/mov 转 webm

文章目录 mp4/mov 转 webm1 目标2 代码3 参考mp4/mov 转 webm 1 目标 把mp4格式的文件、用mov格式的文件转成用webm格式的文件 2 代码 前提是需要先安装好 FFmpeg # 把原始mp4的尺寸变小(带alpha通道的mov,这样转换后好像透明通道丢死了,可以在AE里直接调整输出mov) f…

路由器是什么?

路由器&#xff08;Router&#xff09;是一种网络设备&#xff0c;它的主要功能是用来延伸、拓展网络&#xff0c;特别是在连接多个逻辑上分开的网络时发挥关键作用。逻辑网络可以理解为代表一个单独的网络或者一个子网。当数据需要在不同的子网之间传输时&#xff0c;路由器就…

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

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

【第15章】MyBatis-Plus自动映射枚举

文章目录 前言一、枚举声明1. 方式一&#xff1a;注解标记2. 方式二&#xff1a;实现接口 二、未声明枚举1.修改全局 defaultEnumTypeHandler 三、号外参考: 如何序列化枚举值为前端返回值1.Jackson1.1 重写 toString 方法1.2 注解处理 2.Fastjson2.1 重写 toString 方法 总结 …

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

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

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

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

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

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

百词斩-英语单词大赛

根据您提供的网页内容&#xff0c;以下是一些可能对您有帮助的信息&#xff1a; 1. **比赛名称**&#xff1a; 全国大学生英语单词大赛。 2. **活动平台**&#xff1a; 比赛通过百词斩App进行。 3. **用户基础**&#xff1a; 百词斩App拥有两亿用户&#xff0c;说明这是一个广泛…

HTTP请求中常用的方法

HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超文本传输协议&#xff09;是客户端&#xff08;如浏览器&#xff09;与服务器之间进行通信的基础。HTTP请求中常用的方法主要包括以下几种&#xff1a; 1. GET 方法 用途&#xff1a;用于从服务器获取资源&#xf…

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

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

docker可视化界面portainer

好文转载记录 Docker入门到实践 (九) docker可视化界面portainer的安装与使用_访问 portainer-CSDN博客

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官网” 打开 “轻云处…

Cmake的详细使用

CSDN复制进来的mk笔记很乱 可以看下面的pdf文档 比较清晰 https://download.csdn.net/download/qq_41537499/89512254 Cmake apt install cmake cmake --version cmake version 3.22.1 vim main.cpp main.cpp 代码&#xff1a; #include int main(int argc,char *argv[]) { …

知识图谱知识点总结

知识图谱知识点总结 1. 知识图谱的定义 知识图谱&#xff08;Knowledge Graph&#xff09;是用于描述现实世界中的实体及其关系的图结构&#xff0c;广泛应用于信息检索、自然语言处理、推荐系统等领域。 2. 知识图谱的组成部分 实体&#xff08;Entity&#xff09;: 现实世…

【高中数学/基本不等式】已知: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 …