react中的虚拟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(实际上就是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函数中被创建。比如:

render() {
  return <div id='abc'><span>hello</span></div>
}

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

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的差别

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


key值问题

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

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


参考链接:
虚拟DOM算法的深入剖析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • React非常快速是因为它从不直接操作DOM。 虚拟DOM是在DOM的基础上建立了一个抽象层,对数据和状态所做的任...
    程序人生_小龙阅读 1,361评论 0 1
  • 说在前面 关于 react 的总结过去半年就一直碎碎念着要搞起来,各(wo)种(tai)原(lan)因(le)。心...
    陈嘻嘻啊阅读 6,796评论 7 41
  • 虚拟DOM是什么?一个虚拟DOM(元素)是一个一般的js对象,准备的说是一个对象树(倒立的)虚拟DOM保存了真实D...
    zhangjingbibibi阅读 209评论 0 0
  • 40、React 什么是React?React 是一个用于构建用户界面的框架(采用的是MVC模式):集中处理VIE...
    萌妹撒阅读 975评论 0 1
  • 衷心祈愿 您和您的家人 幸福安康 福慧增长 一切善愿悉皆成就 一切事业无有魔障
    菩提哆啦阅读 153评论 2 1