Vue3.0源码逐行解析(三) 响应系统

485 阅读2分钟

Vue2 跟Vue3 的响应式系统概念从框架本身来看没有任何的改变。

我们来回顾下 Vue2 说:

当你把一个普通的 JavaScript 对象传入 Vue 实例作为data选项,Vue 将遍历此对象所有的 property,并使用Object.defineProperty把这些 property 全部转为getter/setter。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。

每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

Vue3说:

每个组件实例都有一个相应的侦听器实例,该实例将在组件渲染期间把“触碰”的所有 property 记录为依赖项。之后,当触发依赖项的 setter 时,它会通知侦听器,从而使得组件重新渲染。

将对象作为数据传递给组件实例时,Vue 会将其转换为 Proxy。这个 Proxy 使 Vue 能够在 property 被访问或修改时执行依赖项跟踪和更改通知。每个 property 都被视为一个依赖项。

首次渲染后,组件将跟踪一组依赖列表——即在渲染过程中被访问的 property。反过来,组件就成为了其每个 property 的订阅者。当 Proxy 拦截到 set 操作时,该 property 将通知其所有订阅的组件重新渲染。

虽然概念相似但底层实现方式却有本质的不同, Vue2 采用的是 ES5 Object.defineProperty, Vue3 采用的是 ES6 Proxy。 用法也有差异Vue2 推崇optionsAPI , Vue3 推荐 compositionAPI。

Vue2:

var vm = new Vue({
  data:{
   count:1
  }
});

// data 对象上的 property 转换为响应式 property 。

Vue3:

  export default {
    setup() {
      const book = reactive({ title: 'Vue 3 Guide' })
      return {
        book
      }
    }
  }
//reactive函数返回对象的响应式副本, 对象上的 property 转换为响应式 property。

关于Vue2 响应式系统的实现可以看这篇文章。Vue3.0 放弃 Object.defineProperty 你了解多少?

接来下进入 Reactive API

我们先来看一下 reactive 函数的具体实现过程:

 function reactive(target) {
     // if trying to observe a readonly proxy, return the readonly version.
     if (target && target["__v_isReadonly" /* IS_READONLY */]) {
         return target;
     }
     return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers);
 }

reactive 内部通过 createReactiveObject 函数把 target 变成了一个响应式对象。__v_isReadonly 是 ComputedRefImpl 实例才有的属性,如果属性可读不可写,__v_isReadonly 值为 false。

更多信息请参考__v_isReadonly 与 computed
mutableHandlers、 mutableCollectionHandlers 参数对象

createReactiveObject函数:

function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers) {
      if (!isObject(target)) {
          {
              console.warn(`value cannot be made reactive: ${String(target)}`);
          }
          return target;
      }
      // target is already a Proxy, return it.
      // exception: calling readonly() on a reactive object
      if (target["__v_raw" /* RAW */] &&
          !(isReadonly && target["__v_isReactive" /* IS_REACTIVE */])) {
          return target;
      }
      // target already has corresponding Proxy
      const proxyMap = isReadonly ? readonlyMap : reactiveMap;
      const existingProxy = proxyMap.get(target);
      if (existingProxy) {
          return existingProxy;
      }
      // only a whitelist of value types can be observed.
      const targetType = getTargetType(target);
      if (targetType === 0 /* INVALID */) {
          return target;
      }
      const proxy = new Proxy(target, targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);
      proxyMap.set(target, proxy);
      return proxy;
  }

老板叫我暂时不能摸鱼... 喜欢就收藏明天写完。