虚拟 DOM 和 DOM diff

虚拟DOM是一种优化DOM操作的技术,它减少了不必要的DOM更新,实现了跨平台渲染。DOMdiff是虚拟DOM的核心算法,用于比较新旧虚拟DOM树,找出最小变更集。它通过Treediff、Componentdiff和Elementdiff策略进行节点比对。然而,DOMdiff存在同级节点对比的bug,解决方法是使用key来标识子组件。当处理大规模数据时,原生JS操作可能更为稳定。
摘要由CSDN通过智能技术生成

一、虚拟 DOM

1.虚拟DOM的优点

  • 减少DOM操作
    1.虚拟 DOM 可以将多次操作合并为一次操作,比如你添加 1000 个节点,却是一个接一个操作的
    2.虚拟 DOM 借助 DOM diff 可以把多余的操作省掉,比如你添加1000个节点,其实只有10个是新增的
  • 跨平台
    虚拟DOM 不仅可以变成 DOM,还可以变成小程序、iOS应用、安卓应用,因为虚拟DOM本质上是一个JS对象

2.虚拟DOM长什么样

  • React的虚拟DOM
    表示:一个标签为div,子元素为2个span,class为red,点击事件调用一个函数的DOM
const vNode = {
  key: null,
  props: {
    children: [  // 子元素们
       { type: 'span', ... }, 
       { type: 'span', ... }
    ],
    className: "red" // 标签上的属性
    onClick: () => {} // 事件
  },
  ref: null,
  type: "div", // 标签名 or 组件名
  ...
}
  • Vue的虚拟DOM
    和React没什么区别,只是写法不同
const vNode = {
  tag: "div", // 标签名 or 组件名
  data: {
    class: "red", // 标签上的属性
    on: {
      click: () => {} // 事件
    }
  },
  children: [ // 子元素们
    { tag: "span", ... },
    { tag: "span", ... }
  ],
  ...
}

3.如何创建虚拟DOM

  • React.createElement
createElement('div',{className:'red',onClick:()=> {}},[
    createElement('span', {}, 'span1'),
    createElement('span', {}, 'span2')
  ]
)
  • Vue(只能在 render 函数里得到 h)
h('div', {
  class: 'red',
  on: {
    click: () => { }
  },
}, [h('span',{},'span1'), h('span', {}, 'span2'])

4.用 JSX 简化创建虚拟DOM

  • 简化后创建和真实的DOM很像
    真实DOM不支持花括号和里面的JS语法
  • React JSX
    通过 babel 转为 createElement 形式
    React默认就支持JS语法
<div className="red" onClick={fn}>
    <span>span1</span>
    <span>span2</span>
</div>
  • Vue Template
    通过 vue-loader 转为 h 形式
<div class="red" @click="fn">
  <span>span1</span>
  <span>span2</span>
</div>

5.总结

  • 虚拟DOM是什么?
    一个能代表DOM树的对象,通常含有标签名、标签上的属性、事件监听、子元素们,以及其他属性
  • 虚拟DOM有什么优点?
    1.能减少不必要的DOM操作
    2.能跨平台渲染
  • 虚拟DOM有什么缺点?
    1.需要额外创建函数,如 create Element 或 h,但可以通过 JSX 来简化成 XML 写法
    2.XML 写法的缺点:严重依赖打包工具,因为JS不认识该语法

6.额外的小tip

6.1 使用原生JS插入节点

  • JS用时:代码执行完毕,通知浏览器执行
  • 浏览器用时:浏览器真正把节点插到页面中
  • 总结1:JS执行是很快的,只是浏览器在渲染页面的时候让页面不可交互

6.2 使用 React 和 Vue 插入节点

  • 当规模合理的时候,React和Vue的虚拟DOM更快
    因为会减少不必要的操作
  • 当规模很大的时候,原生JS操作更具有稳定性,且浏览器不会崩溃
    Vue也很快,接近原生JS

二、DOM diff

  • 虚拟 DOM 的对比算法

1.什么是 DOM diff

  • 就是一个函数,我们称之为 patch
  • patches = patch(oldVNode,newVNode)
  • patches 就是运行的 DOM 操作
  • 只要给两个虚拟DOM(新旧),就可以返回对应的DOM操作(patches)
    全部执行完才挨个执行
[
  //插入节点
  {type: 'INSERT', vNode: ... },
  //修改文本
  {type: 'TEXT',  vNode: ... },
  //更新属性
  {type: 'PROPS', propsPatch: [...]}
]

2.DOM diff 的大概逻辑

  • Tree diff
    将新旧两棵树逐层对比,找出哪些节点需要更新
    如果节点是组件就看 Component diff
    如果节点是标签就看 Element diff
  • Component diff
    如果节点是组件,就先看组件类型
    类型不同直接替换(删除旧的)
    类型相同则只更新属性
    然后深入组件做 Tree diff(递归)
  • Element diff
    如果节点是原生标签,则看标签名
    标签名不同直接替换,相同则只更新属性
    然后进入标签后代做 Tree diff(递归)

3.DOM diff 的缺点

  • 同级节点对比存在 bug
  • 举例
    1.想要删掉dive中的第一个子元素:span-hello
    2.并非是直接删掉第一个,然后第二个诺过去
    3.真实步骤
    ——对比div:无变化——对比第一个span:无变化——对比第二个span:有变化——对比hello:有变化
    4.执行完后,记录有变化的地方
    ——删掉第二个span
    ——将第一个span的text改为world
    在这里插入图片描述
  • bug
    1.当删掉第二个子组件,却出现第二个子组件的data出现在原来的第三个子组件上
    2.按照上面的步骤,这就是因为删掉的原本就是第三个组件
    3.第二个组件中的input没有被DOM diff修改到。
    在这里插入图片描述
  • 出现bug的原因
    计算机默认以下标来区分元素,而下标是会改变的
  • 解决:给每个子组件添加 key

4.DOM diff 中 key 的问题

  • 主动标记,给子组件取一个名字(key),计算机才能识别删掉的是第二个子组件
  • 步骤
    1.原本数组:[{id: 1,value: 1},{id: 2,value: 2},{id: 3,value: 3}]
    2.点击删除后:[{id: 1,value: 1},{id: 3,value: 3}]
  • 原因
    1.首先计算机发现id从 1 2 3 变成了 1 3
    2.然后一次对比 id:1 和 id:3 的项,发现没有变化
    3.最后,计算机得出结论:第二项被删除了

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值