12道vue高频原理面试题,你能答出几道?

本文深入探讨Vue.js的响应式原理,包括Observer、Dep、Watcher三者的关系以及依赖收集和派发更新的过程。讲解了Vue如何通过Object.defineProperty实现数据劫持,并分析了computed属性的实现机制。此外,对比了computed与watch的区别和应用场景,解释了Vue3.0为何选用Proxy替代Object.defineProperty的原因。同时,讨论了Vue中的key属性在虚拟DOM中的作用,以及nextTick的事件循环和异步更新队列原理。最后,文章还涉及到Vue对数组方法的变异处理和Vue组件data为何必须为函数的原因,以及Vue事件机制的实现。
摘要由CSDN通过智能技术生成
  1. Vue 响应式原理
    vue-reactive
    核心实现类:
    Observer : 它的作用是给对象的属性添加 getter 和 setter,用于依赖收集和派发更新
    Dep : 用于收集当前响应式对象的依赖关系,每个响应式对象包括子对象都拥有一个 Dep 实例(里面 subs 是 Watcher 实例数组),当数据有变更时,会通过 dep.notify()通知各个 watcher。
    Watcher : 观察者对象 , 实例分为渲染 watcher (render watcher),计算属性 watcher (computed watcher),侦听器 watcher(user watcher)三种
    Watcher 和 Dep 的关系
    watcher 中实例化了 dep 并向 dep.subs 中添加了订阅者,dep 通过 notify 遍历了 dep.subs 通知每个 watcher 更新。
    依赖收集
    1.initState 时,对 computed 属性初始化时,触发 computed watcher 依赖收集
    2.initState 时,对侦听属性初始化时,触发 user watcher 依赖收集
    3.render()的过程,触发 render watcher 依赖收集
    4.re-render 时,vm.render()再次执行,会移除所有 subs 中的 watcer 的订阅,重新赋值。
    派发更新
    1.组件中对响应的数据进行了修改,触发 setter 的逻辑
    2.调用 dep.notify()
    3.遍历所有的 subs(Watcher 实例),调用每一个 watcher 的 update 方法。
    原理
    当创建 Vue 实例时,vue 会遍历 data 选项的属性,利用 Object.defineProperty 为属性添加 getter 和 setter 对数据的读取进行劫持(getter 用来依赖收集,setter 用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。
    每个组件实例会有相应的 watcher 实例,会在组件渲染的过程中记录依赖的所有数据属性(进行依赖收集,还有 computed watcher,user watcher 实例),之后依赖项被改动时,setter 方法会通知依赖与此 data 的 watcher 实例重新计算(派发更新),从而使它关联的组件重新渲染。
    一句话总结:
    vue.js 采用数据劫持结合发布-订阅模式,通过 Object.defineproperty 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调
  2. computed 的实现原理
    computed 本质是一个惰性求值的观察者。
    computed 内部实现了一个惰性的 watcher,也就是 computed watcher,computed watcher 不会立刻求值,同时持有一个 dep 实例。
    其内部通过 this.dirty 属性标记计算属性是否需要重新求值。
    当 computed 的依赖状态发生改变时,就会通知这个惰性的 watcher,
    computed watcher 通过 this.dep.subs.length 判断有没有订阅者,
    有的话,会重新计算,然后对比新旧值,如果变化了,会重新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 重新渲染,本质上是一种优化。)
    没有的话,仅仅把 this.dirty = true。 (当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)特性。)
  3. computed 和 watch 有什么区别及运用场景?
    区别
    computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
    watch 侦听器 : 更多的是「观察」的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。
    运用场景
    运用场景:
    当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。
    当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
  4. 为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?
    Object.defineProperty 本身有一定的监控到数组下标变化的能力,但是在 Vue 中,从性能/体验的性价比考虑,尤大大就弃用了这个特性(Vue 为什么不能检测数组变动 )。为了解决这个问题,经过 vue 内部处理后可以使用以下几种方法来监听数组
    push();
    pop();
    shift();
    unshift();
    splice();
    sort();
    reverse();
    复制代码
    由于只针对了以上 7 种方法进行了 hack 处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。
    Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue 2.x 里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择。
    Proxy 可以劫持整个对象,并返回一个新的对象。Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
  5. Vue 中的 key 到底有什么用?
    key 是给每一个 vnode 的唯一 id,依靠 key,我们的 diff 操作可以更准确、更快速 (对于简单列表页渲染来说 diff 节点也更快,但会产生一些隐藏的副作用,比如可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位。)
    diff 算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的 key 与旧节点进行比对,从而找到相应旧节点.
    更准确 : 因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确,如果不加 key,会导致之前节点的状态被保留下来,会产生一系列的 bug。
    更快速 : key 的唯一性可以被 Map 数据结构充分利用,相比于遍历查找的时间复杂度 O(n),Map 的时间复杂度仅仅为 O(1),源码如下:
    function createKeyToOldIdx(children, beginIdx, endIdx) {
    let i, key;
    const map = {};
    for (i = beginIdx; i <= endIdx; ++i) {
    key = children[i].key;
    if (isDef(key)) map[key] = i;
    }
    return map;
    }
    复制代码
  6. 谈一谈 nextTick 的原理
    JS 运行机制
    JS 执行是单线程的,它是基于事件循环的。事件循环大致分为以下几个步骤:
    1.所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
    2.主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
    3.一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
    4.主线程不断重复上面的第三步。
    event-loop
    主线程的执行过程就是一个 tick,而所有的异
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值