面试总结

98 阅读4分钟

Vue 与 React 的区别

  1. Vue 使用模版语法开发,框架本身封装了很多 api(v-if v-show)等可以很方便的去实现数据渲染、数据与试图层是双向绑定(object.defineProperty、getter,setter,发布订阅),不需要特别的优化手段也能达到良好的性能;

  2. React 使用 JSX 语法,框架本身没有 vue 的那些语法糖,数据视图渲染需要自己去完成、React 是单向数据流,强调数据的安全与不可变。只能通过 setState 去改变数据,如果没有使用优化手段可能会导致一些性能上的浪费

React render 执行两次

主要是因为严格模式(StrictMode),在开发环境中通过调用两次的方式将一些隐藏比较深的副作用放大,让开发者更好的发现

class 组件与 function 组件的本质区别

类组件底层只需要实例化一次,实例中保存了组件的 state 等状态,每次更新只需要调用 render 方法以及对应的生命周期;函数组件中,每一次更新都是一次新的函数执行,一次函数组件更新,里面的变量都会重新声明

state 到底是同步还是异步

关键点:batchUpdate(批量更新),批量更新被打破的条件(Promise/setTimeOut)

批量更新

打破批量更新

触发 setState 流程:

  1. 首先判断更新优先级(expirationTime/lane)
  2. 从 fiber 根部开始向下调和子节点,调和阶段对比发生更新的地方,找到发生更新的组件,合并 state,触发 render,获得新的视图,完成 render
  3. commit 阶段,替换真实 dom,完成更新,如果 setState 中有 callback,执行 callback,完成一次 setState 全过程

原理:类组件初始化时绑定了负责更新的 updater 对象,调用 setState 的时候实际上是调用 Updater 对象上的 enqueueSetState 方法


// 创建update,放入当前fiber对象的待更新队列中,开启调度
enqueueSetState(){
     /* 每一次调用`setState`,react 都会创建一个 update 里面保存了 */
     const update = createUpdate(expirationTime, suspenseConfig);
     /* callback 可以理解为 setState 回调函数,第二个参数 */
     callback && (update.callback = callback)
     /* enqueueUpdate 把当前的update 传入当前fiber,待更新队列中 */
     enqueueUpdate(fiber, update);
     /* 开始调度更新 */
     scheduleUpdateOnFiber(fiber, expirationTime);
}


// 通过isBatchingEventUpdates = true 开启事件批量更新,事件结束再通过isBatchingEventUpdates = false 关闭,然后在scheduleUpdateOnFiber中根据开关来确定是否进行批量更新
function batchedEventUpdates(fn,a){
    /* 开启批量更新  */
   isBatchingEventUpdates = true;
  try {
    /* 这里执行了的事件处理函数, 比如在一次点击事件中触发setState,那么它将在这个函数内执行 */
    return batchedEventUpdatesImpl(fn, a, b);
  } finally {
    /* try 里面 return 不会影响 finally 执行  */
    /* 完成一次事件,批量更新  */
    isBatchingEventUpdates = false;
  }
}

在异步环境下继续开启批量更新模式(unstable_batchedUpdates)

setTimeout(()=>{
    unstable_batchedUpdates(()=>{
        this.setState({ number:this.state.number + 1 })
        console.log(this.state.number)
        this.setState({ number:this.state.number + 1})
        console.log(this.state.number)
        this.setState({ number:this.state.number + 1 })
        console.log(this.state.number)
    })
})
//log 0,0,0,callback1,1,callback2,1,callback3,1

React 提供 flushSync 可以将更新任务放在一个较高的优先级中

handerClick=()=>{
    setTimeout(()=>{
        this.setState({ number: 1  })
    })
    this.setState({ number: 2  })
    ReactDOM.flushSync(()=>{
        this.setState({ number: 3  })
    })
    this.setState({ number: 4  })
}
render(){
   console.log(this.state.number)
   return ...
}
// log 3 4 1

函数组件中的批量更新

export default function Index(props){
    const [ number , setNumber ] = React.useState(0)
    /* 监听 number 变化 */
    React.useEffect(()=>{
        console.log('此时的number是:  ' + number )
    },[ number ])
    const handerClick = ()=>{
        /** 高优先级更新 **/
        ReactDOM.flushSync(()=>{
            setNumber(2)
        })
        /* 批量更新 */
        setNumber(1)
        /* 滞后更新 ,批量更新规则被打破 */
        setTimeout(()=>{
            setNumber(3)
        })

    }
    console.log(number)
    return <div>
        <span> { number }</span>
        <button onClick={ handerClick }  >number++</button>
    </div>
}
/*log
    2
    此时number是:2
    1
    此时number是:1
    3
    此时number是:3
*/

注意事项 useState 中,当调用 setState 的时候,在本次函数执行上下文中是获取不到最新的 state 的值

类组件的 setState 和函数组件中的 useState 异同点

相同点:setState 和 useState 底层都调用 scheduleUpdateOnFiber 方法,而且事件驱动的情况下都有批量更新规则

不同点

  1. 在不是 pureComponent 组件的模式下,类组件 setState 不会浅比较两次 state 的值,直接调用 setState,函数组件 useState 中的 setState 会默认比较两次 state 是否相同,再决定是否更新

  2. 类组件 setState 有专门的监听 state 变化的 callback,可以获取最新的 state,函数组件中只能通过 useEffect 来执行 state 变化引起的副作用

  3. 类组件的 setState 底层逻辑是和老的 state 进行合并处理,函数组件的 useState 是重新赋值

生命周期

主要声明周期图

QA

Q:useEffect 和 useLayoutEffect 的区别

A:useLayoutEffect 是同步执行,useEffect 为异步; useLayoutEffect 的执行时机是在 DOM 更新之后,浏览器绘制之前。useEffect 是在浏览器绘制之后,一句话概括就是修改 DOM,改变布局用 useLayoutEffect,其他情况用 useEffect

Q:useEffect 和 componentDidMount/componentDidUpdate 执行时机有什么区别?

A:useEffect 对 React 执行栈来看是异步执行的,而 componentDidMount/componentDidUpdate 是同步执行的,useEffect 不会阻塞浏览器渲染绘制,时机上 componentDidMount/componentDidUpdate 和 useLayoutEffect 更类似