一. JSX 与 虚拟DOM
JSXJSX 是一个看起来很像 XML 的 JavaScript 语法扩展,其本质是 createElement()方法的语法糖 (语法糖:更加直观、简洁、友好)。
虚拟DOM就是一个 JS 对象,用来描述我们希望在页面中看到的 HTML 结构内容.
<1> Why 使用虚拟DOM?
真实 DOM 对象属性多,处理起来繁琐、效率低。更重要的原因:React 要做跨平台开发,而不是被束缚在浏览器端。
如下:我们打印以下一个普通真实 DOM对象 的属性
但是,这些属性,我们肯定不会都用到,真正用到的很少,所以我们可以用一个 JS 对象,来代替该真实 DOM 对象:
该对象有三个属性:
- tagName: 用来表示这个元素的标签名。
- props: 用来表示这元素所包含的属性。
- children: 用来表示这元素的children。
<2> React 使用 JSX 语法的转换过程:
JSX 代码会经过babel-loader 会解析为 React.createElement()嵌套对象。React.createElement() 创建的就是一个虚拟DOM结构。
会被转化为:
栗子:
二. diff 算法 【Reconciliation协调】
算法实现步骤有三步:
- 【首次渲染】用JavaScript对象来表示DOM树的结构; 然后用这个树构建一个真正的DOM树,插入到文档中。
- 当状态变更的时候,重新构造一个新的对象树,然后用这个新的树和旧的树作对比,记录两个树的差异。
- 把2所记录的差异应用在步骤一所构建的真正的DOM树上,视图就更新了。
【首次渲染】
【更新】
<2> 比较两棵树差异的过程
前提:
当状态更新时,react会依据改动后的JSX代码,重新构建一棵 JS 虚拟DOM树,然后将其与之前的老的 JS 虚拟DOM树进行比较!!!
(1) 对两棵树深度优先遍历,记录差异
在深度优先遍历的时候,每遍历到一个节点就把该节点和新的树进行对比,如果有差异的话就记录到一个对象里面。
- 比如div和新的div有差异,当前的标记是0, 那么我们可以使用数组来存储新旧节点的不同
patches[0] = [{difference}, {difference}, ...]
- 同理使用patches[1]来记录p,使用patches[3]来记录ul,以此类推
(2) 差异的类型
1> 替换原来的节点
对于根节点的替换,react 会销毁旧树,重建新树(真实DOM树)
2> 移动、删除、新增子节点
- 在结尾插入节点
可以直接添加!!!只需改变该插入节点,前面都不用变!!!
- 在开始位置插入
需要改变每一个节点,为了解决该性能问题,就引入了key属性!!!
- 说明:key 属性在 React 内部使用,但不会传递给你的组件
- 推荐:在遍历数据时,推荐在组件中使用 key 属性:<li key={item.id}>{item.name}</li>
- 注意:key 只需要保持与他的兄弟节点唯一即可,不需要全局唯一
- 注意:尽可能的减少数组 index 作为 key,数组中插入元素的等操作时,会使得效率低下
3> 修改了节点的属性
直接修改就好
4> 修改文本节点的内容
可以给对应的类型,创建不同的变量来记录!!
则对应的差异记录就可以写为: