原理
render 方法在react里表现的两种形式
class类组件中的 render()
export default class ClassRender extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 1
}
}
render(){
return <div className='cn'>
classRender
<Header> hello </Header>
<div> start </div>
</div>
}
}
函数组件中的函数本身
export default function HookRender(){
return(
<div> hookRender</div>
)
};
1️⃣render后做了什么?
我们在render中编写jsx,通过babel把jsx转译为一个名为React.createElement() 的函数调用。
上述class babel编译后:return(
React.createElement(
'div',
{
className:'cn'
},
'classRender',
React.createElement(
'Header',
null,
'hello'
),
React.createElemnt(
'div',
null,
'start'
)
)
)
React.createElement,从名字上看是创建元素,实际上预先执行了一些检查,帮助编写无错误代码,并创建了一个对象
(简化后的结构)
const element = {
type: 'div',
props:{
className: 'cn',
children:[
'classRender',
{
type: 'Header',
children: 'hello'
},
{
type: 'div',
children: 'start'
}
]
}
}
这些 对象就是 React元素,(VNode)描述出了一个虚拟DOM树。
- 用render新返回的Dom树与旧Dom树进行对比,也就是diff算法。会对新旧两棵树做一个深度优先遍历,这样每一个节点会有一个标记,在深度遍历的时候,每遍历到一个节点,就把该节点和新节点进行对比,如果有差异会放在一个对象里,遍历差异对象,根据差异的类型,根据对应规则更新VDOM
React 的处理 render 的基本思维模式是每次一有变动就会去重新渲染整个应用。在 Virtual DOM 没有出现之前,最简单的方法就是直接调用 innerHTML。Virtual DOM厉害的地方并不是说它比直接操作 DOM 快,而是说不管数据怎么变,都会尽量以最小的代价去更新 DOM。React 将 render 函数返回的虚拟 DOM 树与老的进行比较,从而确定 DOM 要不要更新、怎么更新。当 DOM 树很大时,遍历两棵树进行各种比对还是相当耗性能的,特别是在顶层 setState 一个微小的修改,默认会去遍历整棵树。尽管 React 使用高度优化的 Diff 算法,但是这个过程仍然会损耗性能.
触发时机
1、类组件setState调用时更新render
export default class ClassRender extends React.Component {
constructor(props) {
super(props)
console.log(props)
this.state = {
count: 1,
tag: 'XXXX'
}
this.clickButton = this.clickButton.bind(this);
}
clickButton(){
const { count } = this.state;
const newCount = count < 3 ? count+1 : count;
this.setState({
count : newCount,
})
}
render(){
console.log('classRender', count);
return <div>classRender
<button onClick={this.clickButton}> 点击</button>
{this.state.count}
<TestRender tag={this.state.tag}/>
</div>
}
}
export default function TestRender(props){
console.log('TestRender');
return(
<div> TestRender
{props && props.tag}
</div>
)
};
多次click‘点击’,控制台输出如下:
- setState调用时会触发本组件的render渲染,父组件render时会带着子组件一起render;
- setState值没有发生变化,set同样的值,也会触发本组件render,子组件不管有没有使用相关数据也会跟着渲染。
❗️❗️❗️为了避免不必要的渲染,可以使用shouldComponentUpdate返回false阻止
2、函数组件useState调用时更新render,但是值不发生改变时不会触发render
```javascript export default function HookRender(){ const [count, setCount] = useState(1) const [tag, setTag] = useState(‘999’)
const clickButton = ()=>{ const newCount = count < 3 ? count+1 : count; setCount(newCount) } console.log(‘hookRender’); return(
hookRender {count}) };
export default function TestRender(props){ console.log(‘TestRender’); return(
- useState时数值有变化,render会触发,否则不会触发render
- 父组件render时,子组件会跟着render
结论
综上,render函数里可以编写jsx,在babel作用下转化为createElement形式,用于生成虚拟DOM,最终转化为真实的DOM。
在react中,类组件只要执行了setState方法,就会触发render函数执行。因为React中的shouldComponentUpdate
方法默认返回true,当然,可以手动改写返回false阻止渲染。
函数组件使用useState更改状态不一定·导致重新render。
组件的props发生改变,不一定触发render函数执行,但是如果props的值来自于父组件或者祖先组件的state,在这种情况下父组件或者祖先组件的state发生了改变,就会导致子组件重新渲染。
所以,一旦执行setState就会触发render,useState会判断当前值有无发生改变确定是否执行render方法;一旦父组件发生渲染,子组件也会渲染。