框架源码

Vue

  1. ownKeys 中的追踪是怎么设计的

    ownKeys 拦截的是 Reflect.ownKeys, for…in, Object.keys 等这类遍历属性键的操作,如果有如下代码

    1
    2
    3
    4
    5
    6
    const state = reactive({ a: 1, b: 2 })
    effect(() => {
    for (const key in state) {
    console.log(key)
    }
    })

    此时副作用依赖的是这个“对象的结构”,而不是某个属性值。应对这个场景 Vue 设计了 ITERATE_KEY 来代表对象的结构,当对象的 key 发生变化的时候就会触发 ITERATE_KEY 的 effect。同理在遍历数组键的时候实际关心的是数组的长度,所以只对 length 进行追踪

  2. 响应式原理

    1. ref处理原始值
    2. 对象
  3. computed

    1. 既是effect也是dep
    2. 懒执行:初次访问时才计算 refreshComputed
  4. watch

    1. getter执行的时候收集依赖(effet.run())
    2. 下一个微任务中执行job
    3. schedule异步调度,不会多次调用
  5. nextTick实现

    1. .then
  6. diff算法

    1. type和key相同认为是同一节点
    2. 双端比较+最长子序列
    3. 如果有key的话会通过keyToNewIndexMap索引o1的查找
    4. vue2双层循环查找,时间复杂度高了一个量级
    5. 头部对比,尾部对比,新增,删除,乱序查找最长子序列,从后往前移动

React

  1. React.memo , useMemouseCallback 的作用

    1. React.memo : 缓存组件,可以传入一个判断函数来控制组件是否在父组件重新渲染时渲染。但 React 仍可能会重新渲染它:记忆化是一种性能优化,而非保证。
    2. useMemo : 缓存计算值
    3. useCallback : 缓存函数
  2. fiber

    1. fiber是什么

      从运行机制上来说,fiber是一种流程让出机制,它能够让react的同步渲染中断,将控制权让回浏览器,从而达到不阻塞浏览器的目的

      从数据角度上来说,fiber能够细化成一种数据结构,或者一个执行单元,它是一种增强版的vdom

      从这两个方面来说,react会在跑完一个执行单元的任务之后检测还剩多少时间,如果还有时间就会继续运行,反之就终止任务将控制权转回浏览器,直到下次浏览器自身工作做完有了空闲时间,再将控制权转回react。

    2. requestIdleCallback 和 requestAnimationFrame

      在浏览器空闲时运行,其中callback会接收一个IdleDeadline参数,上面的timeRemaining会返回剩余时间,一般来说剩余时间是1帧耗时-这一帧浏览器渲染的所用耗时,但是在面对长任务的情况下,浏览器会稍微提高一帧耗时到50ms来应对,这样不会导致用户感觉过于卡顿,又能尽量完成任务。

      react实现上采用了requestAnimationframe和messageChannel来模拟requestIdleCallback

    3. 为什么 fiber 用链表组织? fiber通过这种链表网络来模拟传统的js调用栈,传统的js调用栈一旦开始就不能停止,而链表的好处就在于通过提前设置next指针来还原中断之后的节点单元,fiber与fiber之间又存在兄弟父子的关系,可以很自然的形成上下文

    4. 在构建wip的过程中进行diff对比,

      判断是否存在旧有的fiber节点,如果不存在说明没必要diff,直接走fiber新建挂载逻辑。

      child说明有旧有fiber,那就对比key,如果不相等,直接运行deleteChild(returnFiber, child),也就是从div节点的旧有父节点上,将整个div都删除掉,div的子节点都不需要比了,这也验证了react的逐级比较,父不同,子一律都不比较视为不同。

      key相同,那就比较新旧fibertype(标签类型),如果type不相同,跟key不相同一样,调用了deleteRemainingChildren(returnFiber, child)方法,直接从div的旧有父节点上将自己整个删除。

      key type都相同,那只能说明是props变了,因此调用var _existing3 = useFiber(child, element.props)方法,根据新的props来更新旧有的div fiber节点。

  3. 面试官:能说说什么是React的Virtual DOM吗?

    Virtual DOM 是一个用 JavaScript 对象来模拟真实 DOM 的轻量级数据结构。它的核心目的是解决频繁操作真实 DOM 带来的性能问题。因为直接操作真实 DOM 会引发耗时的浏览器重排和重绘,导致页面卡顿。

    Virtual DOM 提供了一个抽象层。当组件状态更新时,框架会生成一个新的 Virtual DOM 树,并与旧树进行高效的 Diff 算法比较,找出需要修改的最小差异。然后,这些差异会被一次性批量应用到真实的 DOM 上。

    这个过程就像是,不是每次都去‘修补’真实的 DOM,而是先在内存中‘打好草稿’,然后一次性完成最终的修改,大大减少了 DOM 操作的次数,从而提升了应用的性能。

  4. 面试官:为什么要用Virtual DOM呢?直接操作DOM不行吗?

    我: 主要是性能考虑。直接操作DOM是很昂贵的操作,特别是频繁的DOM操作会导致页面重排重绘。Virtual DOM的优势在于:首先,它可以批量更新。比如我在一个函数里连续调用几次setState,React会把这些更新合并成一次DOM操作,而不是每次setState都去更新DOM。其次,它有精确的diff算法。React会比较新旧Virtual DOM树,找出真正发生变化的部分,只更新那些需要更新的DOM节点。

  5. 面试官:能具体说说这个diff算法是怎么工作的吗?

    React的diff算法基于三个假设来优化性能:

    1. 第一个是同层比较。React只会比较同一层级的节点,不会跨层级比较,这样复杂度从O(n³)降到O(n)。
    2. 第二个是组件类型比较。如果组件类型不同,React会直接销毁旧组件,创建新组件,不会深入比较。
    3. 第三个就是key的作用。通过key,React可以快速识别哪些元素是新增的、删除的或者移动的。这就是为什么我们在渲染列表时总是被要求加key的原因。
  6. React 19有什么新的改进吗?

    React 19在Virtual DOM这块有几个重要改进。最主要的是自动批处理的优化,现在不管是在事件处理函数里,还是在异步操作中,多个状态更新都会被自动批处理。另外就是并发特性的完善,通过Fiber架构,React可以中断和恢复渲染过程,让高优先级的更新先执行,提升用户体验。

  7. react fiber 工作流程

    当一个状态更新(比如 setState)被触发时,双缓冲模式就开始工作了:

    1. 后台构建:React 从 Current 树的根节点开始,在后台构建一棵 WIP 树。这个过程是增量的、可中断的,即使被暂停,用户看到的仍然是稳定的 Current 树。
    2. 同步更新:当 WIP 树完全构建并准备好后,React 会进入一个名为“提交(Commit)”的阶段。在这个阶段,React 会一次性地、原子性地将 WIP 树反映到真实 DOM 上。
    3. 切换指针:最后,React 会将 Current 树的指针指向新构建好的 WIP 树。现在,WIP 树成了新的 Current 树,而原来的 Current 树则成了旧的树,等待下一次更新时被复用。
  8. 面试官:能说说useLayoutEffect的作用吗?和useEffect有什么区别?

    我: useLayoutEffect和useEffect最大的区别在于执行时机。useEffect是异步执行的,在浏览器绘制完成后执行;而useLayoutEffect是同步执行的,在DOM变更后、浏览器绘制前执行。

    这个差别看起来很小,但在某些场景下很关键。比如需要读取DOM尺寸或者需要同步修改DOM样式时,用useLayoutEffect可以避免页面闪烁。

  9. 面试官:能结合React的Fiber架构解释一下这个执行时机吗?

    我: 好的,这就要从Fiber的工作流程说起了。Fiber架构把React的工作分成两个主要阶段:

    Render阶段是可中断的,React会构建Fiber树,进行diff比较,标记哪些节点需要更新,这个过程可能被高优先级任务打断。

    Commit阶段是不可中断的,会真正应用这些变更到DOM上。Commit阶段又细分为三个子阶段:

    1. Before Mutation阶段:执行getSnapshotBeforeUpdate

    2. Mutation阶段:真正操作DOM,插入、更新、删除节点

    3. Layout阶段:DOM变更已完成,但浏览器还没绘制

    useLayoutEffect就是在Layout阶段同步执行的,这时候DOM已经更新了,但浏览器还没有绘制到屏幕上。而useEffect是在整个Commit阶段完成后,浏览器绘制完成后才异步执行。

  10. 用法:测量dom


感谢您的支持 | Thank you for supporting

框架源码
http://example.com/2025/10/17/5FrameworkAndSourceCode/
作者
Eli Bi
发布于
2025年10月17日
更新于
2025年10月20日
许可协议