JSX 转换 虚拟 DOM
JSX(JavaScript XML) ,在 JS中写 HTML
- JSX中 HTML 本就不是真实的 DOM
- 通过 Babel 转换为 createElement
- createMelment 返回
树状对象数据
这段树状对象数据就是虚拟 DOM
优势
- 跨平台
虚拟 DOM 是一个纯 JavaScript 对象
- 性能优化
频繁的 DOM 操作会导致性能瓶颈,而虚拟 DOM 可以将多次操作合并为一次,减少了重排和重绘的次数
递归渲染虚拟 DOM 树阻塞单线程
虚拟 DOM 中存在三部分
type(元素类型),Property(属性),Children(子元素)
缺点
长时间阻塞主线程,影响浏览器更优先的事件
利用空闲时间渲染
React16将递归的无法中断的更新重构为异步的可中断更新,但用与递归的数据结构无法满足条件(记录当前节点与下一个渲染节点),于是,Fiber结构应相应出来了
Fiber
通过
child,parent,sibling记录当前节点与下一个渲染节点,更好的分块渲染
Fiber 结构
生成 Fiber
- 把元素添加到 dom 中
- 为元素的子元素都创建一个 fiber 结构
- 找到下一个工作单元
空闲时间渲染 Fiber
Render分割为 Render 和 Commit 阶段
上面的 createFiber 里 ,把元素直接添加到 dom 上,因为浏览器随时都有可能中断我们的操作,这样呈现给用户的就是一个不完整的 UI
需要等所有工作单元执行完后,我们再一并进行 所有 dom的添加
Diff 算法
Diff 算法特点
- 分层,同级比较:React 将整个 DOM 树分为多个层级,然后逐层比较,只比较同一层级的节点,从而减少比较的复杂度。同级比较时按照从左到右的顺序进行比较。
- 元素类型对比: 两个不同类型的元素会生成不同的树,如果元素类型发生了变化,React 会销毁旧树并创建新树。
- key 属性:React 使用 key 属性来标识节点的唯一性,从而在比较时能够快速定位到需要更新的节点。
更新 DOM
函数式组件
function App(props) {
return <h1>Hi {props.name}</h1>
}
const element = <App name="foo" />
const container = document.getElementById("root")
Didact.render(element, container)
函数式组件有两点不同,如下:
- 函数式组件没有 dom 节点 ?
- 他的 children 属性 不在 props 上,而是 他的返回值
Hooks
给函数式组件增加 状态(state)
// 保存当前的 fiber
let wipFiber = null
// 保存当前执行 hook 的索引,区分每次执行是哪个 hook
let hookIndex = null
function updateFunctionComponent(fiber) {
wipFiber = fiber
hookIndex = 0
wipFiber.hooks = []
const children = [fiber.type(fiber.props)]
reconcileChildren(fiber, children)
}
实现 useState 钩子
function useState(initial) {
const oldHook =
wipFiber.alternate &&
wipFiber.alternate.hooks &&
wipFiber.alternate.hooks[hookIndex];
const hook = {
// 存在旧值,则直接取,否则取传入的初始值
state: oldHook ? oldHook.state : initial,
// 存放 每次更新 状态的队列
queue: []
};
const actions = oldHook ? oldHook.queue : [];
actions.forEach(action => {
hook.state = action(hook.state);
});
const setState = action => {
hook.queue.push(action);
wipRoot = {
dom: currentRoot.dom,
props: currentRoot.props,
alternate: currentRoot
};
// 设置为下一个 工作单元,这样就可以开启一个新的 渲染
nextUnitOfWork = wipRoot;
deletions = [];
};
wipFiber.hooks.push(hook);
hookIndex++;
return [hook.state, setState];
}