React常见面试题——原理篇

文章目录

  • 函数式编程
  • vdom和diff
  • jsx本质
  • 合成事件
  • setState和batchUpdate
  • transaction事务机制
  • 组件渲染和更新过程

函数式编程

  • 函数式编程是一种编程范式, 对过程进行抽象,将数据以输入输出流的方式封装进过程内部, 从而也降低系统的耦合度。
  • 纯函数:即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用, 副作用包括但不限于:
    • 打印/log
    • 发送一个http请求
    • DOM查询
    • 可变数据
    • 简单一句话, 即只要是与函数外部环境发生交互的都是副作用。
    • 像js中的slice是纯函数,splice则不是纯函数

vdom和diff

  • 对于 vdom 和 diff,h 函数、vdom 数据结构、patch 函数。只比较同一层级,不跨级比较。tag 不相同,则直接删掉重建,不再深度比较。tag 和 key,两者都相同,则认为是相同节点,不再深度比较
    简述diff算法过程.

jsx本质

  • JSX的本质即React.createElement函数,React.createElement 即 h 函数,返回vnode
  • React.createElement的第一个参数是渲染的标签,第二个参数为标签的属性,之后的为子元素
    • 第一个参数,可能是组件,也可能是一个html tag(组件和tag区分是大小写,组件名首字母必须要大写)
  • 函数返回值是一个vnode
  • vnode在通过patch函数进行渲染
// 如下是一个循环的编译
const listElem = <ul>
        {this.state.list.map((item, inedx) => {
          return (<li key={item.id}>
            index: {index}; title:{item.titlle}
          </li>)
        })}
</ul>

// 编译结果如下:
var listElem = /*#__PURE__*/React.createElement("ul", null, (void 0).state.list.map(function (item, inedx) {
  return /*#__PURE__*/React.createElement("li", {
    key: item.id
  }, "index: ", index, "; title:", item.titlle);
}));

合成事件

  • 所有的事件都挂在到document
  • event不是原生的,是SyntheticEvent合成事件对象
  • 和Vue事件不同,和DOM事件也不同
    React常见面试题——原理篇_第1张图片
  • DOM事件会冒泡到document上,document会生成一个统一的React event,在合成事件层会派发事件(通过target可以获取到哪个元素触发的事件,找到这个元素所在组件这个)
  • 为什么要合成事件机制
    • 更好的兼容性和跨平台
    • 挂在到document,减少内存消耗,避免频繁解绑
    • 方便事件的统一管理(如事物机制)

setState和batchUpdate

  • setState有时异步(普通使用),有时同步(setTimeout, 自己定义的DOM事件)

  • 有时合并(对象形式), 有时不合并(函数形式)
    React常见面试题——原理篇_第2张图片

  • 调用了setState之后,newState会被存入pending队列

  • 是否处于batch update机制中

    • 处于batch update机制中, 保存组件与dirtyComponent(state被更新的component)中(走异步操作)
    • 不处于batch update机制中,遍历所有的dirtyComponent,调用updateCompoennt,更新pending state or props(走同步操作)
  • 如何判断是否处于batch update中,是需要根据isBatchingUpdates进行判断

  • 因此:setState无所谓同步和异步,需要看是否命中batchUpdate机制,是否命中batchUpdate机制的判断就是isBatchingUpdate

increase(){
   // 开始:处于batch update
   // isBatchingUpdate = true
   this.setState({
     count: this.state.count+1
   })
   // 结束
   // // isBatchingUpdate = false
 }
  
 // 在increase1中,当执行到setState时,isBatchingUpdate 已经为false
 increase1(){
   // 开始:处于batch update
   // isBatchingUpdate = true
   setTimeout(() => {
     this.setState({
       count: this.state.count+1
     })
   }, 0)
   // 结束
   // // isBatchingUpdate = false
 }
  
componentDidMount(){
    // 开始:处于batch update
    // isBatchingUpdate = true
    document.body.addEventListener('click', () => {
      this.setState({
        count: this.state.count+1
      })
      console.log('count in body event', this.state.count);
    })
    // 结束
    // // isBatchingUpdate = false
  }
  • 那些能命中batchUpdate机制?
    • 生命周期(和它调用的函数)
    • React中注册的事件(和它调用的函数)
    • React可以 “管理” 的入口
  • 哪些不能够命中 batchUpdate 机制
    • setTimeout、setInterval 等和它调用的函数
    • 自定义的 DOM 事件和它调用的函数
    • React 管不到的入口

transaction事务机制

  • 对于 transition事务机制,开始处于 batchUpdateisBatchUpdates = true,其它任何操作,结束,isBatchUpdates = false
increase(){
   // 开始:处于batch update
   // isBatchingUpdate = true
   
   // 其它任何操作
   
   // 结束
   // // isBatchingUpdate = false
 }

组件渲染和更新过程

  • 渲染过程
    • 有了propsstate
    • render()生成vnode
    • patch(elem, vnode)
  • 更新过程
    • setState()—> dirtyComponents(可能有子组件)
    • 执行render函数,根据新的stateprops生成newVnode
    • patch(vnode, newVnode)
  • patch 更新的两个阶段
    • reconciliation阶段——执行diff算法,纯JS计算
    • commit阶段——将diff结果渲染DOM
  • 可能出现的性能问题(如果不将patch更新分为两个阶段的话可能出现的心梗问题)
    • JS 是单线程的,且和 DOM 渲染共用一个线程
    • 当组件足够复杂的时候,组件更新时计算和渲染都压力大
    • 同时再有 DOM 操作需求,如动画、鼠标拖拽等,将卡顿
  • fiber
    • reconciliation阶段进行任务拆分(但是 commit 无法拆分)
    • DOM 需要渲染时暂停更新,空闲时修复
    • 如何知道DOM需要渲染呢?是需要借助于window.requestIdleCallback

你可能感兴趣的:(面试)