React组件与模板重渲染差异:为何表现不同及二者区别?
嘿,这个问题抓得特别准——其实核心是React对**元素(Element)和组件(Component)**的处理逻辑,以及多次调用ReactDOM.render时的实例复用规则,咱们一步步理清楚:
ReactDOM.render调用的重渲染差异根源 先拆解你提到的两种场景的本质:
场景A:复用同一个元素对象
比如你提前声明好元素:const template = <Counter />; // 这里创建了一个React元素对象 ReactDOM.render(template, app); // 后续再次调用时传同一个对象 ReactDOM.render(template, app);这时候每次传给
ReactDOM.render的是同一个元素对象(引用地址不变)。React会识别出这是同一个根元素,于是复用已有的Counter组件实例,只会对比组件内部的虚拟DOM变化,只更新需要变动的元素——这就是你看到的“仅重渲染特定元素”。场景B:每次创建新的元素对象
如果你每次调用都直接写:ReactDOM.render(<Counter />, app); // 后续再次调用还是重新创建元素 ReactDOM.render(<Counter />, app);这里的
<Counter />每次都会被编译成React.createElement(Counter),返回一个全新的元素对象(引用地址不同)。React在根节点做对比时,会认为这是一个“新的根元素”,于是先卸载旧的Counter组件实例(清空state、执行卸载生命周期),再挂载全新的Counter实例——整个组件树被重建,自然触发全量重渲染,也就是你看到的“重渲染整个应用”。
很多人会混淆这两个概念,简单说:
- 元素(Element):是React描述UI的最小单元,本质是普通JavaScript对象,由
React.createElement生成(JSX是它的语法糖)。它记录了要渲染的内容:比如标签类型(div、Counter)、属性、子元素。元素是不可变的,一旦创建就不能修改,更新UI只能通过创建新的元素。 - 组件(Component):是生成元素的“工厂”,可以是函数或者类。它接收
props作为输入,返回一个元素对象。比如function Counter() { return <div>{count}</div>; },这个Counter就是组件,<Counter />就是调用这个工厂生成的元素。
换句话说,你说的“模板”其实就是元素对象,而“组件”是生成这个对象的逻辑。
当你在外部声明const MyElement = <Counter />,这个变量保存了元素对象的引用。每次调用ReactDOM.render(MyElement, app)时,React拿到的都是同一个引用,就会复用之前的组件实例,保留state,只做局部的虚拟DOM diff和更新,自然只会重渲染变化的部分——这和React在组件内部通过setState更新UI的逻辑是一致的:对比新旧元素,局部更新。
内容的提问来源于stack exchange,提问作者Liam

