3-2-13-Vue.js 源码阅读-响应式原理-依赖收集

响应式原理-依赖收集

本篇将详细讲解,在 defineReactive 的 get 中是如何给当前属性收集依赖的。

收集依赖的过程就是通过属性对应的 dep 对象,收集组件对应的 watcher ,把对应的 watcher 添加到属性对应的 subs 数组中。如果模板中对相同的属性使用了两次,在添加依赖的时候只需要添加一次,只要属性发生变化,就去通知对应的 watcher 对象,让 watcher 更新视图。

get: function reactiveGetter () {
  // 如果预定义的 getter 存在,则 value 等于 getter 调用的返回值
  // 否则直接赋予属性值
  const value = getter ? getter.call(obj) : val
  // 如果存在当前依赖目标,即 watcher 对象,则建立依赖
  if (Dep.target) {
    // 通过 depend 这个方法将 target 添加到 dep 的 subs 数组中
    dep.depend()
    if (childOb) {
      // 属性的子属性中也需要收集其父属性的观察者,当子属性发生变化的时候,同样需要给父属性的观察者发送通知
      childOb.dep.depend()
      // 如果属性是一个数组,则特殊处理收集数组对象依赖
      if (Array.isArray(value)) {
        dependArray(value)
      }
    }
  }
  // 返回属性值
  return value
}

在 get 中的这个 Dep.target 就是要收集的观察者,这个观察者是在 Watcher 中赋值的。Watcher 的创建是在 instance/lifecycle.js 中的mountComponent 这个方法中。Watcher 的构造函数中调用了 get 这个方法,这个方法里面会调用 pushTarget 这个方法,将当前创建的 Watcher 实例赋值给全局唯一的 Dep.target。

// Dep.target 用来存放当前正在使用的 watcher
// 全局唯一,并且一次也只能有一个 watcher 被使用
// The current target watcher being evaluated.
// This is globally unique because only one watcher
// can be evaluated at a time.
Dep.target = null
const targetStack = []
// 入栈并将当前 watcher 赋值给 Dep.target
export function pushTarget (target: ?Watcher) {
  // 第一步:入栈
  targetStack.push(target)
  // 第二步: 赋值
  Dep.target = target
}

这里第一步是先入栈的原因是,从 Vue 2.0 开始,每一个组件会对应一个 Watcher 实例,每一个组件都会有一个 mountComponent,在 mountComponent 中创建 Watcher 实例。但是如果组件有嵌套,A组件嵌套了B组件,当渲染A组件的过程中发现还嵌套了B组件,所以就会去先渲染子组件,此时A组件的渲染过程就会被挂载起来,所以A组件对应的 Watcher 对象也应该被存储起来,也就是存到了这个栈中,当子组件渲染完成之后,他会从栈中弹出,继续去完成负组件的渲染,并完成依赖的收集。

知道了 Dep.target 是如何赋值的之后,接下来分析 dep.depend 是如何将 watcher 对象收集起来的。

// dep.js
// 将观察对象和 watcher 建立依赖
depend () {
  if (Dep.target) {
    // 如果 target 存在, 把 dep 对象添加到 watcher 的依赖中
    Dep.target.addDep(this)
  }
}

// 添加新的订阅者 watcher 对象
addSub (sub: Watcher) {
  this.subs.push(sub)
}
// watcher.js
/**
  * Add a dependency to this directive.
  */
addDep (dep: Dep) {
  const id = dep.id
  if (!this.newDepIds.has(id)) {
    this.newDepIds.add(id)
    this.newDeps.push(dep)
    if (!this.depIds.has(id)) {
      dep.addSub(this)
    }
  }
}

可以看到,收集依赖的过程就是在 defineReactive 的 getter 中调用了 depend 这个方法,在这个方法里面通过调用 watcher 的 addDep 这个方法,在 addDep 这个方法中会去判断 当前的 dep 对象是否是已经将当前的 watcher 收集到了,如果没有,在 watcher 的 newDepIds 中记录当前的 dep 对象,并调用 dep.addSub 这个方法将当前的 watcher 对象添加到 dep 的 subs 数组中,完成依赖的收集。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue.js源码全方位深入解析最新下载地址.rar Vue.js源码全方位深入,帮你更深入了解vue 第1章 准备工作 介绍了 Flow、Vue.js源码目录设计、Vue.js源码构建方式,以及从入口开始分析了 Vue.js 的初始化过程。 第2章 数据驱动 详细讲解了模板数据到 DOM 渲染的过程,从 new Vue 开始,分析了 mount、render、update、patch 等流程。 第3章 组件化 分析了组件化的实现原理,并且分析了组件周边的原理实现,包括合并配置、生命周期、组件注册、异步组件。 第4章 深入响应式原理(上) 详细讲解了数据的变化如何驱动视图的变化,分析了响应式对象的创建,依赖收 集、派发更新的实现过程,一些特殊情况的处理,并对比了计算属性和侦听属性的实现,最后分析了组件更新的过程。 第5章 深入响应式原理(下) 详细讲解了数据的变化如何驱动视图的变化,分析了响应式对象的创建,依赖收集、派发更新的实现过程,一些特殊情况的处理,并对比了计算属性和侦听属性的实现,最后分析了组件更新的过程。 第6章 -编译(上) 从编译的入口函数开始,分析了编译的三个核心流程的实现:parse -> optimize -> codegen。 第7章 -编译(下) 从编译的入口函数开始,分析了编译的三个核心流程的实现:parse -> optimize -> codegen。 第8章 -扩展(上) 详细讲解了 event、v-model、slot、keep-alive、transition、transition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 第9章 -扩展(中) 详细讲解了 event、v-model、slot、keep-alive、transition、transition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 第10章 -扩展(下) 详细讲解了 event、v-model、slot、keep-alive、transition、transition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 第11章 Vue-Router 分析了 Vue-Router 的实现原理,从路由注册开始,分析了路由对象、matcher,并深入分析了整个路径切换的实现过程和细节。 第12章 Vuex 分析了 Vuex 的实现原理,深入分析了它的初始化过程,常用 API 以及插件部分的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值