react
React 背景介绍
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 照片墙 的网站。做出来以后,发现这套东西很好用,就在 2013 年 5 月开源了。
什么是 React
- A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES
- 用来构建 UI 的 JavaScript 库
- React 官网
- React 中文文档
- React 中文文档(备选)
特点
- 1 通过 JSX 语法实现组件化开发,为函数式的 UI 编程方式打开了大门
- 2 性能高的让人称赞:通过
diff算法和虚拟DOM实现视图的高效更新 - 3 HTML 仅仅是个开始
> JSX --TO--> EveryThing
- JSX --> HTML
- JSX --> native ios或android中的组件(XML)
- JSX --> VR
- JSX --> 物联网
为什么要用 React
- 1 使用
组件化开发方式,符合现代 Web 开发的趋势 - 2 技术成熟,社区完善,配件齐全,适用于大型 Web 项目(生态系统健全)
- 3 由 Facebook 专门的团队维护,技术支持可靠
- 4 ReactNative - Learn once, write anywhere: Build mobile apps with React
- 5 使用方式简单,性能非常高,支持服务端渲染
- 6 React 非常火,从技术角度,可以满足好奇心,提高技术水平;从职业角度,有利于求职和晋升,有利于参与潜力大的项目
React 中的核心概念
- 1 虚拟 DOM(Virtual DOM)
- 2 Diff 算法(虚拟 DOM 的加速器,提升 React 性能的法宝)
虚拟 DOM(Vitural DOM)
React 将 DOM 抽象为虚拟 DOM,虚拟 DOM 其实就是用一个对象来描述 DOM,通过对比前后两个对象的差异,最终只把变化的部分重新渲染,提高渲染的效率
VituralDOM 的处理方式
- 1 用 JavaScript 对象结构表示 DOM 树的结构,然后用这个树构建一个真正的 DOM 树,插到文档当中
- 2 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 3 把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了
Diff 算法
当你使用 React 的时候,在某个时间点 render() 函数创建了一棵 React 元素树, 在下一个 state 或者 props 更新的时候,render() 函数将创建一棵新的 React 元素树, React 将对比这两棵树的不同之处,计算出如何高效的更新 UI(只更新变化的地方)
了解:
有一些解决将一棵树转换为另一棵树的最小操作数算法问题的通用方案。然而,树中元素个数为n,最先进的算法 的时间复杂度为 O(n3) 。
如果直接使用这个算法,在React中展示1000个元素则需要进行10亿次的比较。这操作太过昂贵,相反,React基于两点假设,实现了一个O(n)算法,提升性能:
- React 中有两种假定:
- 1 两个不同类型的元素会产生不同的树
- 2 开发者可以通过 key 属性指定不同树中没有发生改变的子元素
Diff 算法的说明 - 1
- 如果两棵树的根元素类型不同,React 会销毁旧树,创建新树
// 旧树
<div>
<Counter />
</div>
// 新树
<span>
<Counter />
</span>
执行过程:destory Counter -> insert Counter
Diff 算法的说明 - 2
- 对于类型相同的 React DOM 元素,React 会对比两者的属性是否相同,只更新不同的属性
- 当处理完这个 DOM 节点,React 就会递归处理子节点。
// 旧
<div className="before" title="stuff"></div>
// 新
<div className="after" title="stuff"></div>
只更新:className 属性
// 旧
<div style={{color: 'red', fontWeight: 'bold'}}></div>
// 新
<div style={{color: 'green', fontWeight: 'bold'}}></div>
只更新:color属性
Diff 算法的说明 - 3
- 1 当在子节点的后面添加一个节点,这时候两棵树的转化工作执行的很好
// 旧
<ul>
<li>first</li>
<li>second</li>
</ul>
// 新
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
执行过程:
React会匹配新旧两个<li>first</li>,匹配两个<li>second</li>,然后添加 <li>third</li> tree
- 2 但是如果你在开始位置插入一个元素,那么问题就来了:
// 旧
<ul>
<li>1</li>
<li>2</li>
</ul>
// 新
<ul>
<li>3</li>
<li>1</li>
<li>2</li>
</ul>
执行过程:
React将改变每一个子节点,而非保持 <li>Duke</li> 和 <li>Villanova</li> 不变
key 属性
为了解决以上问题,React 提供了一个 key 属性。当子节点带有 key 属性,React 会通过 key 来匹配原始树和后来的树。
// 旧
<ul>
<li key="2015">1</li>
<li key="2016">2</li>
</ul>
// 新
<ul>
<li key="2014">3</li>
<li key="2015">1</li>
<li key="2016">2</li>
</ul>
执行过程:
现在 React 知道带有key '2014' 的元素是新的,对于 '2015' 和 '2016' 仅仅移动位置即可
- 说明:key 属性在 React 内部使用,但不会传递给你的组件
- 推荐:在遍历数据时,推荐在组件中使用 key 属性:
<li key={item.id}>{item.name}</li> - 注意:key 只需要保持与他的兄弟节点唯一即可,不需要全局唯一
- 注意:尽可能的减少数组 index 作为 key,数组中插入元素的等操作时,会使得效率底下
key demo
- 执行过程:B != A,则创建并插入 B,删除 A;以此类推,创建并插入 A、D、C,删除 B、C、D
- 执行过程:B、D 不做任何操作,A、C 进行移动操作