React render 方法原理与触发时机
理解 React render 方法的工作原理和触发条件
问题
React 的 render 方法是如何工作的?什么情况下会触发重新渲染?
解答
render 方法的两种形式
在类组件中,render 是一个方法:
class Foo extends React.Component {
render() {
return <h1>Foo</h1>;
}
}
在函数组件中,整个函数就是 render:
function Foo() {
return <h1>Foo</h1>;
}
JSX 到虚拟 DOM 的转换
JSX 代码:
return (
<div className='cn'>
<Header>hello</Header>
<div>start</div>
Right Reserve
</div>
)
经过 Babel 编译后转换为:
return (
React.createElement(
'div',
{ className: 'cn' },
React.createElement(Header, null, 'hello'),
React.createElement('div', null, 'start'),
'Right Reserve'
)
)
createElement 接收三个参数:
- type:标签类型
- attributes:标签属性,无则为 null
- children:子节点
这些调用生成虚拟 DOM 树,React 通过 diff 算法比较新旧虚拟 DOM,然后更新真实 DOM。
触发时机
类组件调用 setState
无论状态是否真正改变,都会触发 render:
class Foo extends React.Component {
state = { count: 0 };
increment = () => {
const { count } = this.state;
const newCount = count < 10 ? count + 1 : count;
this.setState({ count: newCount });
};
render() {
console.log("Foo render"); // 每次 setState 都会执行
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
函数组件使用 useState
只有状态值真正改变时才触发 render:
function Foo() {
const [count, setCount] = useState(0);
function increment() {
const newCount = count < 10 ? count + 1 : count;
setCount(newCount); // 值不变时不会触发 render
}
console.log("Foo render");
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>Increment</button>
</div>
);
}
父组件重新渲染
类组件:父组件 render 会导致所有子组件 render
class App extends React.Component {
state = { name: "App" };
render() {
return (
<div className="App">
<Foo /> {/* App render 时,Foo 也会 render */}
<button onClick={() => this.setState({ name: "App" })}>
Change name
</button>
</div>
);
}
}
函数组件:使用 useState 时,只有首次和状态改变时才触发子组件 render
function App() {
const [name, setName] = useState('App');
return (
<div className="App">
<Foo /> {/* 首次渲染后,点击按钮不会触发 Foo render */}
<button onClick={() => setName("aaa")}>
{name}
</button>
</div>
);
}
关键点
- render 方法将 JSX 转换为
createElement调用,生成虚拟 DOM,通过 diff 算法更新真实 DOM - 类组件执行
setState必定触发 render,即使状态值未改变 - 函数组件使用
useState时,只有状态值真正改变才触发 render - 类组件的父组件重新渲染会导致所有子组件重新渲染
- 函数组件的渲染行为更加优化,避免不必要的重复渲染
目录