Skip to main content

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是在初次渲染时调用组件两次,还是在重渲染的时候调用组件两次

    • 都是两次