框架操作的是啥?——虚拟DOM
虚拟DOM 的作用是啥?
性能
先在虚拟DOM 上进行一次 diff 算法,就不用在真实 DOM 上操作本不需要更新的东西
跨平台
虚拟DOM 意味着 他不是真正的DOM 这句话听起来有点废话
总之就是 他有利于跨平台开发
哪来的虚拟DOM?—— dsl 编译
dsl 是 domain specific language ,领域特定语言
html、css、都能算是 web dsl
像前面的 vdom,虽然经常听到,但是我们肯定不会说自己手写一个JS对象当作 vdom 的
vdom 就是虚拟DOM
像 react 就是写 jsx 编译为 vdom
其实上一句话不太准确,应该是 编译为 一个 渲染函数 render function 然后执行结果返回一个 vdom
所以平时我们写的 非常接近 html 的 jsx
vue 的 模板 也差不多把 差别:
- vue template compiler 自家团队写的
- react jsx 靠 babel
react 里 Babel 就会将 jsx 编译为 React.createElement(...)
然后这个函数执行结果 就是 vdom
谁来把 虚拟DOM 渲染 真实 DOM
到最后 肯定还是要回到 DOM API 来进行修改、把最终 DOM 展现出来
- document.createElement 创建元素
- setAttribute 设置属性
- addEventListener 设置事件监听器
- …
然后 渲染 也要根据 哪里发生了什么变化来进行渲染
这些修改标志 都是有 tag 的,具体操作也就是根据 传入的 tag 来进行对应的操作 —— 也就是最平凡 的 switch case …
- HostComponent 就是元素
- HostText 就是文本
- FunctionComponent 函数组件
- ClassComponent 类组件
组件怎么渲染?
其实就是调用对应的 函数、class形式的 render 函数
也就是说 组件实际上就是 vdom 不同形式的封装
状态管理
react 通过 setState api 触发状态更新,更新之后重新渲染
为什么 vue 可以做到精准地更新变化的组件
vue 的状态管理是 响应式的
不论是哪里的组件,只要用到了对应的状态就要作为依赖存储起来,状态变化就触发相应的render
而 react 某个组件的更新 也有可能触发其他位置的更新,但是 react 不知道,所以他就要 更新全部
这应该 是 框架之间最大的区别了
react 架构
react 15
- Reconciler(协调器)—— 负责找出变化的组件
- 调用函数组件、或class组件的render方法,将返回的JSX转化为虚拟DOM
- 将虚拟DOM和上次更新时的虚拟DOM对比
- 通过对比找出本次更新中变化的虚拟DOM
- 通知Renderer将变化的虚拟DOM渲染到页面上
- Renderer(渲染器)—— 负责将变化的组件渲染到页面上
协调器和渲染器是交替工作的!
它是递归执行的,层级很深时,JS执行时间就超过了 16ms ,交互就会卡顿
JS 执行 和 GUI 渲染是不能同时的,也就是说 JS 执行会阻塞 渲染
15 缺点以及改进方向
因为 react 状态管理的缘故, react 修改了东西,必然是要渲染全部 vdom,计算的总量是没机会缩减的
但是又想要不阻塞加载,那要怎么样?
那干脆将全部的工作量分散到每个 16ms 中
也就是将计算分多次操作
但是直接打断,也不行,react 15 架构是无法支持 异步更新的。
直接打断意味着 DOM 渲染可能是不完全的,因为协调器和渲染器是交替工作的
react 16 fiber 架构
- Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
- 功能更完备的 requestIdleCallback polyfill — 当浏览器有剩余时间时通知我们。
- Reconciler(协调器)—— 负责找出变化的组件
- 从原来的 递归渲染改为 循环
workLoop()
渲染- 每次循环中都会调用 一个
shouldYield
方法来判断当前是否有剩余时间
- 每次循环中都会调用 一个
- 从原来的 递归渲染改为 循环
- Renderer(渲染器)—— 负责将变化的组件渲染到页面上
调度器和协调器
react 16 架构 ,协调器和渲染器不是交替进行的。
当调度器将任务交给 协调器,协调器会为发生变化的 vdom 打上相应的 tag
调度器和协调器都是在 内存中执行,
是可以被打断的:
- 有更高优先级的任务
- 当前帧没有剩余时间了
因为没有真正触碰到 DOM,所以中断后不会导致 用户看到更新不完全的 DOM
fiber 链表结构
fiber 即是上面那种 渲染流程,也指这个链表结构
打断后,还要想办法得到父节点和兄弟节点啊
所以一个节点具有这几个指针
- childre 记录子节点
- sibling 记录 兄弟节点
- return 记录 父节点
并且如果发生了更改,还会加上标记,然后将该节点放进 effectList
渲染器执行三小段
渲染器执行的时候,也就是 commit 阶段 ,其中又分为三小段: