React背景
React 起源于 Facebook内部项目,因为该公司对市场上所有 JavaScript MVC 框架都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。
React特点
- 使用
jsx语法创建组件,实现组件化开发,使用函数式的UI编程 - 性能高: 通过
虚拟DOM和diff算法实现视图的搞笑更新
虚拟DOM(Virtual DOM)
React将浏览器原生DOM 抽象为 虚拟DOM,通过对比前后两个对象的差异,最终只把变化的部分重新渲染,提高渲染效率
虚拟DOM的处理方式
- 1、用JavaScript对象结构表示DOM树的结构,然后用这个树去构建一个真正的DOM树,插入到文档当中
- 2、当状态变更时,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 3、把
2记录的差异使用1的步骤构建到真正的DOM树上,实现视图更新
Diff算法
当你使用React的时候,在某个时间使用 render()函数创建了一棵React元素树,在下一个state或者props更新时,render()函数会创建一棵新的React元素树,React将对比这两棵树的不同之处,计算出如何更高效的更新 UI(只更新变化的地方)
React中有两种假定:
- 根元素不同,结构树一定不同
- 开发者可以通过key属性指定不同树中没有发生改变的子元素(设置key属性)
Diff算法说明
第一种情况
如果两颗树的根节点元素类型不同,React会销毁旧树,创建新树
// 旧树
<div>
<Counter />
</div>
// 新树
<span>
<Counter />
</span>
此时React的执行过程: destory 旧树 ---> insert 新树
第二种情况
- 对于根节点类型相同的React DOM元素, React会对比两者的根节点的属性是否相同,只更新不同的属性
- 当处理完这个DOM节点,React就会递归处理自己子节点
// 旧
<div className="before"></div>
// 新
<div className="after"></div>
此时React的执行过程: 只更新 className属性
第三种情况
- 当在子节点的后面插入一个新的节点
// 旧
<ul>
<li>1</li>
<li>2</li>
</ul>
// 新
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
此时React的执行过程:
<li>1</li>对比<li>1</li><li>2</li>对比<li>2</li>- insert
<li>3</li>
React会对比新旧的两个树,然后添加<li>3</li>
- 但是如果此时在子节点的开始位置插入一个元素
// 旧
<ul>
<li>1</li>
<li>2</li>
</ul>
// 新
<ul>
<li>insert</li>
<li>1</li>
<li>2</li>
</ul>
此时React的执行过程:
<li>1</li>对比<li>insert</li><li>2</li>对比<li>1</li>- insert
<li>2</li>React会对比新旧两个树,发现对应不上,会将每个子节点删除重新创建
key属性
为了解决上面的问题,React提供了一个key属性。当子节点带有key属性,React会通过key来匹配新旧树
key属性只会在React内部使用,不会通过props传递给组件
// 旧
<ul>
<li key="1">1</li>
<li key="2">2</li>
</ul>
// 新
<ul>
<li key="3">insert</li>
<li key="1">1</li>
<li key="2">2</li>
</ul>
此时React的执行过程: React通过key属性定位到只有 key="3"的元素是新的,对于 key="1"和 key="2"的元素,只需要移动位置即可
注意
- key属性只需要与他的兄弟节点
保持唯一,不需要全局唯一 - 不要使用数组的index作为key,数组插入或者移除元素时,index会改变