
React 元素是普通对象
const element = <h1 title="foo">你好</h1>官方在 Writing Markup with JSX 那页说得很直接:JSX 看起来像 HTML,但在底层会被转换成普通的 JavaScript 对象。useMemo 参考页里给过这个对象的形状:
{ type: List, props: { items: visibleTodos } }type 是元素类型,props 是属性与子节点。它没有方法,也不指向任何 DOM 节点。这棵由对象组成的树,社区叫它虚拟 DOM 树。
官方讲的是三步,不是「算法」
Render and Commit 把一次界面更新拆成三步:
- 触发。初次渲染调用
root.render();之后的渲染由某处 state 更新排入。 - 渲染。这一步是 React 调用你的组件。初次渲染从根组件开始;后续渲染从「state 变了的那一个组件」开始,再递归到它返回的子组件。
- 提交。React 改 DOM。初次渲染用
appendChild把节点放上屏幕;重渲染时「应用必要的最小改动,这些改动是渲染期间算出来的」。
官方还有一句最该记住的:两次渲染结果相同,React 就不碰 DOM。
它举的例子是一个每秒重渲染一次的组件,里面有个 <input>。你往输入框里打的字不会消失,因为 React 看到 input 还在原来的位置,就没动它,也没动它的 value。
那「比较」按什么规则
现行文档把这件事讲成位置与身份。Preserving and Resetting State 说 React 按组件在树里的位置判断身份:同一个位置上的同一个组件保留 state,key 就是用来改变这个判断的。
至于「不同类型产出不同树、同类型只更新属性、按 key 匹配子节点」这套启发式规则,只存在于已归档的旧站文档:Reconciliation。那页顶部挂着「This site is no longer updated」的横幅,官方把它指向上面那篇。要引它就注明已归档,别当成现行规范。
用代码看一眼元素长什么样
const element = React.createElement( 'div', { id: 'box' }, React.createElement('span', null, '你好'))createElement 现在被官方归在 Legacy API 里,日常写 JSX 不用直接调它,工具链会生成等价的调用。上面的写法与 React.createElement('div', { id: 'box' }, React.createElement('span', null, '你好')) 得到的对象,和 Babel 或 SWC 从 JSX 生成的是一回事,细节见本系列的 Babel 与 SWC。
跨平台那件事
元素只是描述,把它变成什么由渲染器决定:react-dom 产出 DOM 节点,React Native 产出原生视图。这是抽象层带来的收益,与性能无关。
三个容易被讲糊的说法
「虚拟 DOM 比直接操作 DOM 快」不准确。官方的口径是重渲染时只应用必要的最小改动,而不是每次重建整棵 DOM。在很简单的场景里,手写 DOM 操作可能更快。
「有虚拟 DOM 才能跨平台」也不严谨。让 React Native 成立的是组件模型与渲染器的分离,虚拟 DOM 只是这条路上的一个中间产物。
「diff 是 O(n) 算法」出自那页已归档的文档,它依赖两条假设:不同类型的元素产出不同的树,开发者用 key 提示哪些子节点是稳定的。官方现在不再讲这套,引它可以,但要说清来源。
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 192 天
部分内容可能已过时