JSX 的本质
JSX 代码本身并不是 HTML,也不是 Javascript,在渲染页面前,需先通过解析工具(如babel)解析之后才能在浏览器中运行。
babel官网可查看 JSX 解析后的效果
更早之前,Babel 会把 JSX 转译成一个 React.createElement()
函数调用,功能与现在的 jsxs 函数类似
React.createElement() 的语法
相当于 vue 的 h 函数
- 第1个参数:标签名(字符串),或组件(变量)
- 第2个参数:属性为key 的对象
- 之后的参数是子元素,多个子元素,也可以用数组包裹放在第三个元素的位置
- 返回 vnode
例如
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
会被Babel 转译为
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
React.createElement() 会创建对象
// 注意:这是简化过的结构
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
这些对象被称为 “React 元素”。它们描述了你希望在屏幕上看到的内容。React 通过读取这些对象,然后使用它们来构建 DOM 并保持随时更新。
SyntheticEvent 合成事件机制
react 中的事件,不是原生事件(vue 中是原生事件),而是SyntheticEvent 合成事件。
为什么要用合成事件机制 ?
- 更好的兼容性和跨平台(自定义封装的合成事件对象,更方便添加兼容性和跨平台的相关代码)
- 统一挂载到 document 或 id 为root 的根节点 (从 react 17 开始是 root ),可以减少内存消耗,避免频繁解绑
- 方便事件的统一管理(如事务机制)
为什么从 react 17 开始,要改为绑定到 root?
因为这样更利于多个 React 版本并存,例如微前端。
组件渲染过程
- 通过 props ,state 初始化变量
- 通过 render() 函数生成虚拟节点 vnode
- 通过 patch(elem, vnode) 函数(不一定叫 patch,具体可能是其他名字,功能相同)渲染页面
组件更新过程
- 通过 setState(newState) 修改响应式变量,生成 dirtyComponents (可能有子组件)
- 通过 render() 函数生成虚拟节点 vnode
- 通过 patch(elem, vnode) 函数(不一定叫 patch,具体可能是其他名字,功能相同)渲染页面
更新的 patch 分为两个阶段
- reconciliation 阶段-执行 diff 算法,纯 JS 计算
- commit 阶段-将 diff 结果渲染 DOM
之所以分为了两个阶段,是为了方便 fiber 优化性能
fiber 优化性能的原理
fiber 是 React 的内部运行机制
- 将 reconciliation 阶段拆分成多个任务(commit 阶段无法拆分)
- 当 DOM 需要渲染时暂停 reconciliation 中的任务,等无需进行 DOM 渲染时继续执行 reconciliation 中的任务
通过 window.requestIdleCallback 可获知 DOM 是否需要渲染,但 window.requestIdleCallback 有的浏览器不支持,所以在不支持window.requestIdleCallback 的浏览器上,无法使用 fiber 优化性能
哪些场景需要 fiber 优化性能 ?
因 JS 是单线程,且和 DOM 渲染共用一个线程,当组件足够复杂,组件更新时,计算和渲染的压力都很大,同时再有 DOM 操作需求(动画,鼠标拖拽等),就很容易出现页面卡顿(DOM 渲染等待 JS 计算)