[译]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.rhkb.cn/news/367839.html

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

相关文章

Servlet_Web小结

1.web开发概述 什么是服务器&#xff1f; 解释一&#xff1a;服务器就是一款软件,可以向其发送请求,服务器会做出一个响应. 可以在服务器中部署文件,让他人访问 解释二&#xff1a;也可以把运行服务器软件的计算机也可以称为服务器。 web开发&#xff1a; 指的是从网页中向后…

Android LayoutInflater 深度解析

在 Android 开发中&#xff0c;LayoutInflater 是一个非常重要的工具。它允许我们从 XML 布局文件中动态地创建 View 对象&#xff0c;从而使得 UI 的创建和管理更加灵活。本文将深入解析 android.view.LayoutInflater&#xff0c;包括它的基本用法、常见问题以及高级用法。 什…

idea xml ctrl+/ 注释格式不对齐

处理前 处理后 解决办法 取消这两个勾选

【UE5.3】笔记6-创建可自由控制Pawn类

搭建场景 搭建一个场景&#xff1a;包含地板、围墙。可以根据喜好加一些自发光的效果。 增加食物 创建食物蓝图类&#xff0c;在场景里放置一些食物以供我们player去吃掉获取分值。 创建可控制的layer 我们先右键创建一个蓝图继承自pawn类&#xff0c;起名BP_Player&#xf…

深度学习之半监督学习:一文梳理目标检测中的半监督学习策略

什么是半监督目标检测&#xff1f; 传统机器学习根据训练数据集中的标注情况&#xff0c;有着不同的场景&#xff0c;主要包括&#xff1a;监督学习、弱监督学习、弱半监督学习、半监督学习。由于目标检测任务的特殊性&#xff0c;在介绍半监督目标检测方法之前&#xff0c;我…

视频融合共享平台LntonCVS统一视频接入平台智慧安防应用方案

安防视频监控平台LntonCVS是一款拥有强大拓展性和灵活部署能力的综合管理平台。它支持多种主流标准协议&#xff0c;包括国标GB28181、RTSP/Onvif、RTMP等&#xff0c;同时兼容各厂家的私有协议和SDK&#xff0c;如海康Ehome、海大宇等。LntonCVS不仅具备传统安防视频监控功能&…

PHP电商系统开发指南最佳实践

电子商务系统开发的最佳实践包括&#xff1a;数据库设计&#xff1a;选择适合关系型数据库&#xff0c;优化数据结构&#xff0c;考虑表分区&#xff1b;安全&#xff1a;加密数据&#xff0c;防止 sql 注入&#xff0c;处理会话管理&#xff1b;用户界面&#xff1a;遵循 ux 原…

mysql-sql-第十四周

学习目标&#xff1a; sql 学习内容&#xff1a; 40.查询学过「哈哈」老师授课的同学的信息 Select * from students left join score on students.stunmscore.stunm where counm (select counm from teacher left join course on teacher.teanmcourse.teanm where teache…

【深度学习】Transformer

李宏毅深度学习笔记 https://blog.csdn.net/Tink1995/article/details/105080033 https://blog.csdn.net/leonardotu/article/details/135726696 https://blog.csdn.net/u012856866/article/details/129790077 Transformer 是一个基于自注意力的序列到序列模型&#xff0c;与基…

伺服调试三环讲解

在伺服调试过程中,有些项目要求不高,采用伺服自整定就可以调试好伺服,但有些项目对伺服有着比较高的要求,于是需要采取手动调试伺服参数,下面就介绍一下伺服三环参数的调试的方法。 三环指:电流环、速度环、位置环 带宽关系:电流环带宽>速度环带宽>位置环带宽 三环控…

C语言单链表的算法之插入节点

一&#xff1a;访问各个节点中的数据 &#xff08;1&#xff09;访问链表中的各个节点的有效数据&#xff0c;这个访问必须注意不能使用p、p1、p2&#xff0c;而只能使用phead &#xff08;2&#xff09;只能用头指针不能用各个节点自己的指针。因为在实际当中我们保存链表的时…

偏微分方程笔记

极小位能原理&#xff1a; C 2 C^2 C2 是一个集合符号&#xff0c;表示所有二阶连续可微函数的集合 弱导数 C 2 C^2 C2 是一个集合符号&#xff0c;表示所有二阶连续可微函数的集合。 C 0 ∞ ( I ) C^{\infty}_0(I) C0∞​(I)表示于 I I I上无穷可微&#xff0c;且在端点a&…

使用pyinstaller 如何打包python项目

参考&#xff1a;【python项目正确打包方法-哔哩哔哩】 https://b23.tv/EDB6zbG Pyinstaller 详解多种打包过程(去坑,填坑)。_pyinstaller -f -w-CSDN博客 1.打开命令提示符&#xff1a; 找到python项目所在位置&#xff0c;输入cmd即可 2. 安装pipenv: 在命令提示符&#…

【强化学习的数学原理】课程笔记--2(贝尔曼最优公式,值迭代与策略迭代)

目录 贝尔曼最优公式最优 Policy求解贝尔曼最优公式求解最大 State Value v ∗ v^* v∗根据 v ∗ v^* v∗ 求解最佳 Policy π ∗ \pi^* π∗一些证明过程 一些影响 π ∗ \pi^* π∗ 的因素如何让 π ∗ \pi^* π∗ 不 “绕弯路” γ \gamma γ 的影响reward 的影响 值迭…

UiPath+Appium实现app自动化测试

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

Visual Studio 中的键盘快捷方式

1. Visual Studio 中的键盘快捷方式 1.1. 可打印快捷方式备忘单 1.2. Visual Studio 的常用键盘快捷方式 本部分中的所有快捷方式都将全局应用&#xff08;除非另有指定&#xff09;。 “全局”上下文表示该快捷方式适用于 Visual Studio 中的任何工具窗口。 生成&#xff1…

第十四章 集合(List)

一、集合框架体系 集合&#xff1a; &#xff08;1&#xff09;可以动态保存任意多个对象 &#xff08;2&#xff09;提供了一系列方便的操作对象的方法 二、Collection 1. Collection 接口常用方法 &#xff08;1&#xff09;add&#xff1a;添加单个元素 &#xff08;2…

Cypress测试:7个快速解决问题的调试技巧!

以下为作者观点&#xff1a; 快速编写代码是一项宝贵的技能&#xff0c;但能够有效调试和解决错误和bug&#xff0c;更是一个软件开发人员具有熟练技能的标志。调试是开发过程中的一个关键环节&#xff0c;可以确保软件按预期运行并满足用户需求。 Cypress 调试简介 Cypress …

【Linux】—Xshell、Xftp安装

文章目录 前言一、下载Xshell、Xftp二、安装Xshell三、使用XShell连接Linux服务器四、修改windows的主机映射文件&#xff08;hosts文件&#xff09;五、远程连接hadoop102/hadoop103/hadoop104服务器六、安装Xftp 前言 XShell远程管理工具&#xff0c;可以在Windows界面下来访…

uniapp + vue3 + Script Setup 写法变动 (持续更新)

一、uniapp 应用生命周期&#xff1a; https://uniapp.dcloud.net.cn/tutorial/vue3-composition-api.html 注意&#xff1a; 应用生命周期仅可在App.vue中监听&#xff0c;在其它页面监听无效。 二 、uniapp页面生命周期&#xff1a; https://uniapp.dcloud.net.cn/tutori…