props, state与render函数关系 – 数据和页面是如何实现互相联动的?

时间:2021-09-13
本文章向大家介绍props, state与render函数关系 – 数据和页面是如何实现互相联动的?,主要包括props, state与render函数关系 – 数据和页面是如何实现互相联动的?使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

react是由数据驱动的框架,当数据发生变化页面就会自动的发生变化。它背后的原理,,, 数据和页面联动的机理

当组件的state或者props发生改变的时候,render函数就会重新执行,页面就会从新被渲染,因为页面是由render函数渲染出来的。同时,当父组件的render函数被运行时,它的子组件的render都将被重新运行一次

什么是虚拟DOM
加入没有react,我们自己实现这个功能,思路大概是: 1,state 数据 2,JSX模板 3,数据 + 模板结合,生成真实的DOM,显示在页面上 4,state 发生改变 5,数据 + 模板 结合,生成真实的DOM,替换原始的DOM(这里耗费了大量的性能) 缺陷: 第一次生成了一个完整的DOM片段 第二次生成了一个完整的DOM片段 第二次的DOM替换第一次的DOM,非常耗性能。

改良: 1,state 数据 2,JSX模板 3,数据 + 模板结合,生成真实的DOM,显示在页面上 4,state 发生改变 5,数据 + 模板 结合,生成真实的DOM,并不直接替换原始的DOM 6,新的DOM(这个DOM是JS底层里的DocumentFragment叫文档碎片,它是在内存里,并没有被真实地挂载到页面) 和 原始的DOM 做比对,找差异(DOM层在做比对,耗性能) 7,找出input框发生了变化 8,只用新的DOM中的input元素,替换掉老的DOM中的input元素 这种改良虽然多了几个步骤,但是性能上确实有了提升,因为在用新的DOM替换老的DOM的时候,涉及到替换DOM的操作或者DOM比对的操作是比较耗性能的,所以第五步替换整个原始的DOM耗费了大量的性能,这里新的做法是不去替换原始的DOM,而是拿新的DOM和原始的DOM做比对,只需替换原始DOM改变后的input一小部分,这样DOM替换的内容变少了,这块性能得到了提升。但是问题又来了,虽然这里节约了DOM替换的性能,但是又损耗了新的DOM和原始DOM做比对的性能。这样的性能的提升并不是很明显。 缺陷: 性能的提升并不是很明显

react提出了第三种方案,就是虚拟DOM方案: 1,state 数据 2,JSX模板 3,数据 + 模板结合,生成真实的DOM,显示在页面上

hello world

4,数据 + 模板 结合 ,生成虚拟DOM(虚拟DOM就是一个JS对象,用它来描述真实DOM)(虽然这里生成了虚拟DOM,但是这里的性能损耗极小,用js生成一个js对象它的代价是很小的,但是用js生成一个DOM元素它的代价极高,因为底层原理是,js创建一个js对象其实很简单,但是创建一个DOM的时候它要调用web application级别的api,这种级别的api它的性能损耗是比较大的)

['div',{id:'abc'},['span',{},'hello world']]

5,state 发生改变 6,数据 + 模板 结合 ,生成新的虚拟DOM(极大地提升了性能)

['div',{id:'abc'},['span',{},'happy chen']]

7,比较原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中的内容(比较原始js对象和新的js对象,两个js的比对是不消耗性能的,和前面的DOM结构比对相比极大地提升了性能) 8,直接操作DOM,改变span中的内容

总结: react中引入虚拟DOM,减少了真实DOM的创建以及真实DOM的比对,取而代之我创建的都是js对象,比对的也都是对象,通过这种方式react底层极大地提高了性能。本质上就是js里面去比较js对象不怎么耗性能,但是比较真实的DOM会很耗性能。

实际上react底层真正的实现第三步和第四步是反过来的,

深入了解虚拟DOM
react提出了第三种方案,就是虚拟DOM方案:

1,state 数据 2,JSX模板 3,数据 + 模板 结合 ,生成虚拟DOM

['div',{id:'abc'},['span',{},'hello world']]

4,用虚拟DOM的结构,生成真实的DOM,显示在页面上

<div id="abc"><span>hello world</span></div>

5,state 发生改变 6,数据 + 模板 结合 ,生成新的虚拟DOM(极大地提升了性能)

['div',{id:'abc'},['span',{},'happy chen']]

7,比较原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中的内容 8,直接操作DOM,改变span中的内容

在react中 JSX 到虚拟DOM(js对象)到真实的DOM

虚拟DOM的好处: 1,性能提升了,DOM的比对变成了js对象的比对 2,它使得跨端应用得以实现。React Native,我们可以用react去写原生的应用。之所以可以用react语法去写原生应用得益于虚拟DOM的存在,因为渲染DOM在浏览器是没有问题的,但是在移动端的原生应用里面是不存在DOM这个概念的,生成的DOM在原生应用里面就无法使用,但是js对象在原生应用里面是可以被识别的,这个时候虚拟DOM(也就是js对象)就可以生成原生应用里的组件,这样原生的应用里面也可以把页面展示出来,使得react既可以生成网页应用也可以生成原生应用。

虚拟DOM中的Diff算法
react提出了第三种方案,就是虚拟DOM方案:

1,state 数据 2,JSX模板 3,数据 + 模板 结合 ,生成虚拟DOM

['div',{id:'abc'},['span',{},'hello world']]

4,用虚拟DOM的结构,生成真实的DOM,显示在页面上

<div id="abc"><span>hello world</span></div>

5,state 发生改变 6,数据 + 模板 结合 ,生成新的虚拟DOM(极大地提升了性能)

['div',{id:'abc'},['span',{},'happy chen']]

7,比较原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中的内容

8,直接操作DOM,改变span中的内容

当数据发生改变时虚拟DOM才会去比对,要么改变了state,要么改变了props,其实props的改变也是因为父组件的state发生了改变,所以归根结底是 setState方法调用改变state时数据发生变化,然后虚拟DOM才去比对。 setState是异步的,异步是为了提高react底层的性能。比如我连续调用了三次setState,变更三组数据,这三次调用的时间间隔非常小,如果我们每次都去比对就会消耗性能。所以react会把这三次setState合并成一个setState只去做一次虚拟DOM的比对然后更新DOM,这样就会省去额外的两次虚拟DOM的比对。所以setState做成异步本质上是为了提高react底层的性能。

Diff算法里有个很重要的概念是同级比较。 图上,左侧是一个虚拟DOM,当数据发生改变时,右侧是新的虚拟DOM,然后两个虚拟DOM做比对。找到差异之后去更新真实的DOM。在做比对的时候会做同层的比对,首先会比较最顶层的DOM节点,假设它俩一致再去比对第二层。如果不一致,react就不会再继续往下比对了,它发现第一层DOM是有差异的,会把原始页面上虚拟DOM对应的所有下面节点的DOM全部都删除掉重新生成新的第一层节点下的虚拟DOM,然后用重新生成的DOM替换原始的DOM。也就是它发现这层虚拟DOM不一致的话下面就不会再比对了。虽然下面的节点有可能是一样的,但是这种同层比对的算法更加简单,我只需要一层一层的做对比就好了,算法简单带来的好处就是比对的速度会非常的快。 在做虚拟DOM节点循环时,我们可以给每个节点取个名字。这个时候虚拟DOM的比对会根据key值做关联,如上图,我们就可以很快的发现只增加了一个Z。所以之前虚拟DOM节点名字a,b,c,d,e,到了新的虚拟DOM节点名字还是a,b,c,d,e,,只是改变的那个节点不同。所以我们在写代码的时候key值不要是index,因为这样你就无法保证原始虚拟DOM的key值和新的虚拟DOM的key值一致了。 总结: setState异步可以把多次setState结合成一次setState,减少虚拟DOM比对的次数。在虚拟DOM比对的时候会有同层比对的概念,也就是Diff算法比对两个虚拟DOM差异的时候它会逐层的比对,如果一层不一致下面就不会再比对了,直接废弃掉用新的替换老的就可以了,这样会提高性能。react循环中引入key值是为了提高react虚拟DOM比对的性能,所以key值要保持稳定,在项目中能不用index做key值就一定不要用index做key值,因为index值是不稳定的。

原文地址:https://www.cnblogs.com/art-poet/p/15263273.html