欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

React常见面试题——原理篇

程序员文章站 2022-06-13 10:37:19
...

函数式编程

  • 函数式编程是一种编程范式, 对过程进行抽象,将数据以输入输出流的方式封装进过程内部, 从而也降低系统的耦合度。
  • 纯函数:即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用, 副作用包括但不限于:
    • 打印/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常见面试题——原理篇
  • DOM事件会冒泡到document上,document会生成一个统一的React event,在合成事件层会派发事件(通过target可以获取到哪个元素触发的事件,找到这个元素所在组件这个)
  • 为什么要合成事件机制
    • 更好的兼容性和跨平台
    • 挂在到document,减少内存消耗,避免频繁解绑
    • 方便事件的统一管理(如事物机制)

setState和batchUpdate

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

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

  • 调用了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 管不到的入口

trantransaction事务机制

  • 对于 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
相关标签: 面试