渲染与提交
一次界面更新怎样从请求到显示?
- 触发: 首次调用 root.render,或组件及祖先的状态更新,向 React 请求工作;
- 渲染: React 调用组件,递归计算这一次 UI 应该是什么;
- 提交: React 把需要的变更应用到 DOM,随后浏览器按自己的流程绘制像素;
状态更新 / 首次渲染
↓
调用组件,计算 UI
↓
提交需要的 DOM 变更
↓
浏览器绘制
重新渲染是否意味着重新创建全部 DOM?
- 组件调用: 父组件渲染时通常继续渲染它返回的子组件,性能优化可以减少不必要的工作;
- DOM 复用: 重新执行函数不等于替换所有节点,React 会尽量保留可复用 DOM,只提交必要差异;
- 可见结果: 如果输出相同,一次重新渲染可能不产生任何 DOM 修改,因此不要仅凭日志次数推断页面重建;
function Clock({ time }) {
return (
<section>
<h1>{time}</h1>
<input aria-label="草稿" />
</section>
);
}
- 观察方式: 先在输入框输入草稿,再改变 time;只要树中身份保持一致,标题更新不应清空输入框,说明函数重新执行与 DOM 重建不是同一件事;
为什么渲染阶段不能直接操作外部世界?
- 可重试计算: 一次渲染可能被重做或放弃,计算结果未必提交;在这里发请求或改 DOM 会留下与最终 UI 不匹配的副作用;
- 纯度规则: 相同输入与局部修改的具体要求见保持组件纯粹;
如何理解提交与浏览器绘制的区别?
- DOM 提交: 更新页面节点与属性,使 DOM 对应新的 React 输出;
- 浏览器绘制: 浏览器根据 DOM、样式和布局把像素显示出来,它是与 React 计算不同的阶段;
- Effect 时机: 不能把所有 Effect 简化为“绘制后执行”,需要按 useEffect 与 useLayoutEffect 的契约选择,见 Effect Hooks;
何时才需要优化渲染?
- 先测量: 发现可感知延迟后用 Profiler 定位昂贵子树,确认成本来自计算、DOM、网络还是其他环节;
- 优化方向: 保持状态局部、减少无谓 Effect,再评估缓存或非阻塞更新;不要为了减少日志而改变正确语义;