React哲学
DOM与虚拟DOM
DOM
- 为了提高性能需要减少浏览器重新绘制页面的次数
- 回流:元素的大小或者位置发生变化
- 重绘:元素样式发生改变
-
如何减少浏览器回流次数?
-
读写分离
-
将改变样式的代码放一块,读取数据的代码放一块。以下代码会回流两次,如果没有console则只会回流一次
div.style.width='50px';
console.log(div.clientWidth);
div.style.height='50px';
div.style.margin='5px';
-
-
元素批量处理
- 创建一个对象,将所有变化的节点放到这个对象然后一次性将这个对象提交到DOM
-
-
总结,DOM的回流次数较多,导致反复渲染影响性能
虚拟DOM
- 虚拟DOM创建了真实DOM的树结构,更轻量,删除了很多不必要的信息。更新时将新旧虚拟DOM使用Diff算法比较,最后才更新到真实DOM,通过这种方式大大减少了需要修改的节点的数量,也减少了回流次数,在少量更新的情况下相比原来提高了性能。
- 相比手动操作原生DOM,React让开发者只用考虑组件的状态而不用去操心渲染过程,所以使用React就不要用原生DOM的API啦。
- 虚拟DOM的在内存中比较相比原生DOM比较更快
为什么要React
- 只要会HTML、JS、CSS再加上JSX语法以及一些钩子如useState、useEffect、useRef基本上就掌握了React。简单易学,你只需要。。。一点点规则
渲染机制
-
什么时候会触发渲染?(还能什么时候,第一次要渲染吧?组件的数据(state,props)更新了要渲染吧?)
-
初次渲染(整个APP)
-
组件或者其某个祖先的状态发生了变化
-
useState
-
设置 state 不会更改现有渲染中的变量,但会请求一次新的渲染并加入渲染队列。以下代码点击按钮后显示的数字在原来的基础上加1。
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
</>
)
} -
React 会在事件处理函数(更改state)执行完成之后处理 state 更新。这被称为批处理。以下代码在两次
alert之后才会渲染新的值import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
alert("render before")
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
alert("renderafter")
}}>+3</button>
</>
)
} -
要在一个事件中多次更新某些 state,你可以使用
setNumber(n => n + 1)更新函数。以下代码点击按钮后显示的数字在原来的基础上加3
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
}}>+3</button>
</>
)
} -
只有在树中相同的位置渲染相同的组件时,React 才会一直保留着组件的 state。如果被移除或者渲染了别的组件都会移除state。
- 如果想在相同位置渲染同一个组件但是不同状态怎么办?
- 给组件绑定Key值,React就会认为这是两个不同的组件
- 将组件渲染在不同位置
- 如果想在相同位置渲染同一个组件但是不同状态怎么办?
-
-
useEffect
- useEffect更新会在组件渲染出来之后,再执行,如果useEffect里面更新的state可能会导致不必要的重新渲染。
-
-
-
React如何渲染?
-
在进行初次渲染时调用根组件
-
后续的渲染React会调用内部状态更新触发了渲染的函数组件
-
这个过程时递归,渲染的组件还有子组件,那么子组件还会渲染,某些情况下会跳过渲染
-
React仅在渲染之间存在差异时才会更改DOM节点,也就是说组件的位置和状态在虚拟DOM中没有发生变化那么他就不会变,哪怕父组件的状态变了
-
export default function Clock({ time }) {
return (
<>
<h1>{time}</h1>
<input />
</>
);
}
# input标签在time变化时不会重新渲染
-
-
-
更新DOM
- 初次渲染,React使用
appendChild()API 将所有节点渲染出来 - 重渲染,React将在渲染的时候计算最少的必要操作更新DOM(过程在上面)
- 初次渲染,React使用
严格模式
-
严格模式下,React是在初次渲染时调用组件两次,还是在重渲染的时候调用组件两次
- 都是两次