前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >react中的虚拟DOM

react中的虚拟DOM

作者头像
EchoROne
发布2022-08-15 08:28:02
7780
发布2022-08-15 08:28:02
举报
文章被收录于专栏:玩转大前端

数据驱动原理

假如让我们自己实现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(实际上就是DocumentFragment),和原始的DOM做比对,找差异

7. 找出input框发生了变化

8. 只用新的DOM中的input元素,替换掉老的DOM中的input元素

缺陷:

性能的提升并不明显

- 用虚拟DOM:

·1. state数据

·2. JSX模板

·3. 数据 + 模板相结合,生成虚拟DOM(虚拟DOM就是一个js对象,用它来描述真实的DOM),比如

['div', {id: 'abc'}, ['span', {}, 'hello']](损耗了极小的性能)

·4.用虚拟DOM的结构生成真实的DOM

<div id = 'abc'><span>hello</span></div>

·5. state 发生变化

·6. 数据 + 模板 生成新的虚拟DOM (极大地提升了性能)

['div', {id: 'abc'}, ['span', {}, 'bye']]

·7. 比较原始虚拟DOM新的虚拟DOM的区别,找到区别是span中的内容(极大地提升了性能)

·8. 直接操作DOM,改变span中得内容

优点:

1. 性能提升了

2. 它使得跨端应用得以实现,由此产生React Native。因为原生应用中是没有DOM这个概念的,不过虚拟DOM的js对象可以被正常识别,因此只要加一层判断辨别是浏览器还是原生app即可将虚拟DOM的思想引入从而使react可以开发原生app

那么,react是在哪里创建虚拟dom的呢?

每次react中的state或者props改变时会触发组件中的render函数,父组件触发render函数时子组件也会跟着触发render函数,而虚拟DOM 即是在render函数中被创建。比如:

代码语言:javascript
复制
render() {
  return <div id='abc'><span>hello</span></div>
}

上图中return的内容是JSX模版,实际上底层实现是用 React.createElement 创建,其接收三个参数,第一个是创建的标签,第二个是它的属性,第三个是它的内容

代码语言:javascript
复制
render() {
  return React.createElement('div', {id: 'abc'}, React.createElement('span', {}, 'hello'))
}

上面两段代码是等价的,JSX模版其实只是react为了让我们开发更简单便捷,其底层还是用 React.creatElement 这个api构建的,即

JSX -> createElement -> 虚拟DOM(js对象) -> 真实DOM

虚拟DOM中的diff算法

用虚拟DOM完成数据驱动涉及到关键的一点就是我们如何比较两个虚拟DOM的差异。

首先我们得确定发生差异的来由,归根结底是组件的state发生了变化,调用了setState方法,之后我们就会生成新的虚拟DOM与旧的进行比对

可以试想,若调用了三个setState方法,那么我们就得生成三次、比对三次,而且如果调用的时间过短的话,无疑会给比对增加许多压力,消耗性能。react旧的setState方法接收的是一个对象,难免就会遇到上述问题,react16中则建议将setState方法的参数改成一个函数,其变成了一个异步方法,即三个setState会自动合成一个setState,生成一次虚拟DOM比对一次差异,这是新的api带来的性能优化

1. 同级比较

diff算法中只会比较同层级的元素,一旦发现某一级之间有所不同,则会弃置其子级,直接用从新的差异的一级以及其下的所有子级替换老的。我们会有个疑问,这样做那子级中相同的元素不是无法复用了吗,那怎么还能提高比对性能?这无疑是一种缺陷,但也带来了好处就是算法实现简单,也就提高了比对速度,因此最后也是提升了性能的

2. 引用key值

for循环中如果没有给每个item所在标签增加一个key值,vue和react中都会发出警告,建议我们加上,这是因为当进行虚拟DOM比对时,我们需要比较出相同的元素和不同的,没有key我们就很难一一对应,需要做两层循环比较,用上了key值则我们可以清楚比较出哪一个新增或删除了什么,就像下图

有了key值我们就可以轻易判别z是新加的元素从而找出了差异。有一个注意点就是开发中有些小白喜欢用index做key值,这是不建议的。就像下图

如果我们创建了a、b、c三个item,key值分别定义为其index:0、1、2

当我们删除了a,则b、ckey值变为了0、1,则每一项之间无法根据key值一一对应起来了,失去了key值存在的意义。因此建议是用稳定的值作为key值,比如特有的id

虚拟dom以及其diff算法是react框架中的底层原理,腾讯面试官面试前端时也曾问过,无非就是告诉我们不能只会用,还要往深处去钻,了解原理开发遇到bug才不会手足无措,才有可能快速调试处bug的原因。比如不懂引用类型的深拷贝浅拷贝原理,当我们发现复制过来的值改变了,原来的值也发生了变化就会匪夷所思。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-11-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数据驱动原理
  • 虚拟DOM中的diff算法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档