实现一个简版的react框架(1)-理解VDOM, Diff, JSX

marvin

marvin

实现一个简版的react框架(1)-理解VDOM, Diff, JSX

React 以其简洁的 API 设计, 单向数据流的思想, 深受开发者的喜爱, 其高性能的基础来源于其背后的虚拟DOM以及高效的 Diff 算法, 下面就来看看这些概念在 React 中是如何体现的

虚拟DOM

虚拟DOM是一种模型, 每一个虚拟DOM实际上都对应到页面上的某一个真实存在的元素, 并保留了一些相关的信息

VDOM

上图左边就是浏览器中真实的 DOM, 右侧是内存中的虚拟 DOM, 每个元素的结构都是一样的, 例如 ul 元素在虚拟 DOM 中的结构为:

{ 
  Type: 'ul', 
  Props: { class: 'todos' },
  Children: [...]
}

用js对象来描述DOM对象的方式就是虚拟DOM的本质

Diff 算法

diffing 是一个在两次渲染过程中识别有变化的元素并进行相应操作的一个过程, 例如对添加节点, 删除节点, 替换节点, 更新节点等的 diff, 例如我们有以下列表:

<ul>
	<li>1</li>
  <li>2</li>
</ul>

在对ul列表进行不同的操作的时候, diff算法的的策略是不一样的, 下面就来看看不同操作diff的具体过程

在列表最后添加节点

如果向 ul 列表中新增一个 li 节点, 则新添加的 li 节点将直接被插入 ul 的最后作为最后一个项目

在列表中间添加节点

如果在1和2之间添加一个新的li节点, 那么这个新的节点将直接替换掉内容为2的节点, 新的计算出来的内容为2的节点将直接添加到ul的最后, 简单来说就是 替换添加

在没有 keys 的情况下删除节点

如果将内容为 1 的元素删除了, 则旧 DOM 中内容为1的元素将被新的内容为2的元素替换, 且替换的地方后面的元素都直接删除, 简单来说就是 替换删除

在没有 keys 的情况下删除节点会有一个问题, 因为这里没有 key 作为唯一依据, 当需要删除1的时候, 由于1被2直接替换了, 因此实际上触发 componentWillUnmount 的元素并不是 1, 而是2, 因此在循环渲染 jsx 元素的过程中 keys 是很重要的属性, 可以避免这个问题

jsx

当我们在 react 中写类似 html 标签的时候, 实际上就是在使用 jsx

什么是 jsx

jsx 是一种用类似 html 标签的方式来描述原生 DOM 元素或组件的一种语法, 在这里大写的标签始终被当做组件, 小写的标签被当做原生DOM元素, 它的本质只是 React.createElement 的语法糖, 这些标签都会通过 React.createElement 生成 VDOM

jsx 的特点

除了直接写标签, 还可以在 jsx 中使用表达式, 表达式中可以写任何 javascript 代码, 而且 jsx 本身也是表达式, 简称 jsx 表达式, 在 jsx 中使用表达式只需要在jsx中插入一对花括号, 花括号中写表达式, 最重要的一点是 jsx 在渲染的时候只能有一个根元素, 为了解决这个问题 React 提供了一个占位标签, 这个标签并不会生成 DOM 元素

写在最后

在理解了以上几个重要概念之后, 接下来就可以将目光聚焦在框架的实现上了