Vue源码探究-数据绑定的实现

本篇代码位于vue/src/core/observer/

在总结完数据绑定实现的逻辑架构一篇后,已经对Vue的数据观察系统的角色和各自的功能有了比较透彻的了解,这一篇继续仔细分析下源码的具体实现。

Observer


  1. // Observer类用来附加到每个观察对象上。
  2. // 将被观察目标对象的属性键名转换成存取器,
  3. // 以此收集依赖和派发更新
  4. /**
  5. * Observer class that is attached to each observed
  6. * object. Once attached, the observer converts the target
  7. * object's property keys into getter/setters that
  8. * collect dependencies and dispatch updates.
  9. */
  10. // 定义并导出 Observer 类
  11. export class Observer {
  12. // 初始化观测对象,依赖对象,实例计数器三个实例属性
  13. value: any;
  14. dep: Dep;
  15. vmCount: number; // number of vms that has this object as root $data
  16. // 构造函数接受被观测对象参数
  17. constructor (value: any) {
  18. // 将传入的观测对象赋予实例的value属性
  19. this.value = value
  20. // 创建新的Dep依赖对象实例赋予dep属性
  21. this.dep = new Dep()
  22. // 初始化实例的vmCount为0
  23. this.vmCount = 0
  24. // 将实例挂载到观测对象的'__ob__‘属性上
  25. def(value, '__ob__', this)
  26. // 如果观测对象是数组
  27. if (Array.isArray(value)) {
  28. // 判断是否可以使用__proto__属性,以此甚至augment含糊
  29. const augment = hasProto
  30. ? protoAugment
  31. : copyAugment
  32. // 拦截原型对象并重新添加数组原型方法
  33. // 这里应该是为了修复包装存取器破坏了数组对象的原型继承方法的问题
  34. augment(value, arrayMethods, arrayKeys)
  35. // 观察数组中的对象
  36. this.observeArray(value)
  37. } else {
  38. // 遍历每一个对象属性转换成包装后的存取器
  39. this.walk(value)
  40. }
  41. }
  42. // walk方法用来遍历对象的每一个属性,并转化成存取器
  43. // 只在观测值是对象的情况下调用
  44. /**
  45. * Walk through each property and convert them into
  46. * getter/setters. This method should only be called when
  47. * value type is Object.
  48. */
  49. walk (obj: Object) {
  50. const keys = Object.keys(obj)
  51. for (let i = 0; i < keys.length; i++) {
  52. // 将每一个对象属性转换成存取器
  53. defineReactive(obj, keys[i])
  54. }
  55. }
  56. // 观察数组对象
  57. /**
  58. * Observe a list of Array items.
  59. */
  60. observeArray (items: Array<any>) {
  61. // 遍历每一个数组对象,并继续观察
  62. for (let i = 0, l = items.length; i < l; i++) {
  63. observe(items[i])
  64. }
  65. }
  66. }
  67. // 下面是两个辅助函数,用来根据是否可以使用对象的 __proto__属性来拦截原型
  68. // 函数比较简单,不详细解释了
  69. // helpers
  70. /**
  71. * Augment an target Object or Array by intercepting
  72. * the prototype chain using __proto__
  73. */
  74. function protoAugment (target, src: Object, keys: any) {
  75. /* eslint-disable no-proto */
  76. target.__proto__ = src
  77. /* eslint-enable no-proto */
  78. }
  79. /**
  80. * Augment an target Object or Array by defining
  81. * hidden properties.
  82. */
  83. /* istanbul ignore next */
  84. function copyAugment (target: Object, src: Object, keys: Array<string>) {
  85. for (let i = 0, l = keys.length; i < l; i++) {
  86. const key = keys[i]
  87. def(target, key, src[key])
  88. }
  89. }
  90. // observe函数用来为观测值创建观察目标实例
  91. // 如果成功被观察则返回观察目标,或返回已存在观察目标
  92. /**
  93. * Attempt to create an observer instance for a value,
  94. * returns the new observer if successfully observed,
  95. * or the existing observer if the value already has one.
  96. */
  97. // 定义并导出observe函数,接受观测值和是否作为data的根属性两个参数
  98. // 返回Observer类型对象或空值
  99. export function observe (value: any, asRootData: ?boolean): Observer | void {
  100. // 判断是否为所要求的对象,否则不继续执行
  101. if (!isObject(value) || value instanceof VNode) {
  102. return
  103. }
  104. // 定义Observer类型或空值的ob变量
  105. let ob: Observer | void
  106. // 如果观测值具有__ob__属性,并且其值是Observer实例,将其赋予ob
  107. if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
  108. ob = value.__ob__
  109. } else if (
  110. // 如果shouldObserve为真,且不是服务器渲染,观测值是数组或者对象
  111. // 观测值可扩展,且观测值不是Vue实例,则创建新的观察目标实例赋予ob
  112. // 这里发现了在Vue核心类创建实例的时候设置的_isVue的用途了
  113. shouldObserve &&
  114. !isServerRendering() &&
  115. (Array.isArray(value) || isPlainObject(value)) &&
  116. Object.isExtensible(value) &&
  117. !value._isVue
  118. ) {
  119. ob = new Observer(value)
  120. }
  121. // 如果asRootData为真且ob对象存在,ob.vmCount自增
  122. if (asRootData && ob) {
  123. ob.vmCount++
  124. }
  125. // 返回ob
  126. return ob
  127. }
  128. // defineReactive函数用来为观测值包赚存取器
  129. /**
  130. * Define a reactive property on an Object.
  131. */
  132. // 定义并导出defineReactive函数,接受参数观测源obj,属性key, 值val,
  133. // 自定义setter方法customSetter,是否进行递归转换shallow五个参数
  134. export function defineReactive (
  135. obj: Object,
  136. key: string,
  137. val: any,
  138. customSetter?: ?Function,
  139. shallow?: boolean
  140. ) {
  141. // 创建依赖对象实例
  142. const dep = new Dep()
  143. // 获取obj的属性描述符
  144. const property = Object.getOwnPropertyDescriptor(obj, key)
  145. // 如果该属性不可配置则不继续执行
  146. if (property && property.configurable === false) {
  147. return
  148. }
  149. // 提供预定义的存取器函数
  150. // cater for pre-defined getter/setters
  151. const getter = property && property.get
  152. const setter = property && property.set
  153. // 如果不存在getter或存在settter,且函数只传入2个参数,手动设置val值
  154. // 这里主要是Obserber的walk方法里使用的情况,只传入两个参数
  155. if ((!getter || setter) && arguments.length === 2) {
  156. val = obj[key]
  157. }
  158. // 判断是否递归观察子对象,并将子对象属性都转换成存取器,返回子观察目标
  159. let childOb = !shallow && observe(val)
  160. // 重新定义属性
  161. Object.defineProperty(obj, key, {
  162. enumerable: true,
  163. configurable: true,
  164. // 设置getter
  165. get: function reactiveGetter () {
  166. // 如果预定义的getter存在则value等于getter调用的返回值
  167. // 否则直接赋予属性值
  168. const value = getter ? getter.call(obj) : val
  169. // 如果存在当前依赖目标,即监视器对象,则建立依赖
  170. if (Dep.target) {
  171. dep.depend()
  172. // 如果子观察目标存在,建立子对象的依赖关系
  173. if (childOb) {
  174. childOb.dep.depend()
  175. // 如果属性是数组,则特殊处理收集数组对象依赖
  176. if (Array.isArray(value)) {
  177. dependArray(value)
  178. }
  179. }
  180. }
  181. // 返回属性值
  182. return value
  183. },
  184. // 设置setter,接收新值newVal参数
  185. set: function reactiveSetter (newVal) {
  186. // 如果预定义的getter存在则value等于getter调用的返回值
  187. // 否则直接赋予属性值
  188. const value = getter ? getter.call(obj) : val
  189. // 如果新值等于旧值或者新值旧值为null则不执行
  190. /* eslint-disable no-self-compare */
  191. if (newVal === value || (newVal !== newVal && value !== value)) {
  192. return
  193. }
  194. // 非生产环境下如果customSetter存在,则调用customSetter
  195. /* eslint-enable no-self-compare */
  196. if (process.env.NODE_ENV !== 'production' && customSetter) {
  197. customSetter()
  198. }
  199. // 如果预定义setter存在则调用,否则直接更新新值
  200. if (setter) {
  201. setter.call(obj, newVal)
  202. } else {
  203. val = newVal
  204. }
  205. // 判断是否递归观察子对象并返回子观察目标
  206. childOb = !shallow && observe(newVal)
  207. // 发布变更通知
  208. dep.notify()
  209. }
  210. })
  211. }
  212. // 下面是单独定义并导出的动态增减属性时观测的函数
  213. // set函数用来对程序执行中动态添加的属性进行观察并转换存取器,不详细解释
  214. /**
  215. * Set a property on an object. Adds the new property and
  216. * triggers change notification if the property doesn't
  217. * already exist.
  218. */
  219. export function set (target: Array<any> | Object, key: any, val: any): any {
  220. if (process.env.NODE_ENV !== 'production' &&
  221. (isUndef(target) || isPrimitive(target))
  222. ) {
  223. warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  224. }
  225. if (Array.isArray(target) && isValidArrayIndex(key)) {
  226. target.length = Math.max(target.length, key)
  227. target.splice(key, 1, val)
  228. return val
  229. }
  230. if (key in target && !(key in Object.prototype)) {
  231. target[key] = val
  232. return val
  233. }
  234. const ob = (target: any).__ob__
  235. if (target._isVue || (ob && ob.vmCount)) {
  236. process.env.NODE_ENV !== 'production' && warn(
  237. 'Avoid adding reactive properties to a Vue instance or its root $data ' +
  238. 'at runtime - declare it upfront in the data option.'
  239. )
  240. return val
  241. }
  242. if (!ob) {
  243. target[key] = val
  244. return val
  245. }
  246. defineReactive(ob.value, key, val)
  247. ob.dep.notify()
  248. return val
  249. }
  250. // Delete函数用来对程序执行中动态删除的属性发布变更通知,不详细解释
  251. /**
  252. * Delete a property and trigger change if necessary.
  253. */
  254. export function del (target: Array<any> | Object, key: any) {
  255. if (process.env.NODE_ENV !== 'production' &&
  256. (isUndef(target) || isPrimitive(target))
  257. ) {
  258. warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
  259. }
  260. if (Array.isArray(target) && isValidArrayIndex(key)) {
  261. target.splice(key, 1)
  262. return
  263. }
  264. const ob = (target: any).__ob__
  265. if (target._isVue || (ob && ob.vmCount)) {
  266. process.env.NODE_ENV !== 'production' && warn(
  267. 'Avoid deleting properties on a Vue instance or its root $data ' +
  268. '- just set it to null.'
  269. )
  270. return
  271. }
  272. if (!hasOwn(target, key)) {
  273. return
  274. }
  275. delete target[key]
  276. if (!ob) {
  277. return
  278. }
  279. ob.dep.notify()
  280. }
  281. // 特殊处理数组的依赖收集的函数,递归的对数组中的成员执行依赖收集
  282. /**
  283. * Collect dependencies on array elements when the array is touched, since
  284. * we cannot intercept array element access like property getters.
  285. */
  286. function dependArray (value: Array<any>) {
  287. for (let e, i = 0, l = value.length; i < l; i++) {
  288. e = value[i]
  289. e && e.__ob__ && e.__ob__.dep.depend()
  290. if (Array.isArray(e)) {
  291. dependArray(e)
  292. }
  293. }
  294. }

Dep


  1. let uid = 0
  2. // dep是个可观察对象,可以有多个指令订阅它
  3. /**
  4. * A dep is an observable that can have multiple
  5. * directives subscribing to it.
  6. */
  7. // 定义并导出Dep类
  8. export default class Dep {
  9. // 定义变量
  10. // 私有变量,当前评估watcher对象
  11. static target: ?Watcher;
  12. // dep实例Id
  13. id: number;
  14. // dep实例监视器/订阅者数组
  15. subs: Array<Watcher>;
  16. // 定义构造器
  17. constructor () {
  18. // 初始化时赋予递增的id
  19. this.id = uid++
  20. this.subs = []
  21. }
  22. // 定义addSub方法,接受Watcher类型的sub参数
  23. addSub (sub: Watcher) {
  24. // 向subs数组里添加新的watcher
  25. this.subs.push(sub)
  26. }
  27. // 定义removeSub方法,接受Watcher类型的sub参数
  28. removeSub (sub: Watcher) {
  29. // 从subs数组里移除指定watcher
  30. remove(this.subs, sub)
  31. }
  32. // 定义depend方法,将观察对象和watcher建立依赖
  33. depend () {
  34. // 在创建Wacther的时候会将在创建的Watcher赋值给Dep.target
  35. // 建立依赖时如果存在Watcher,则会调用Watcher的addDep方法
  36. if (Dep.target) {
  37. Dep.target.addDep(this)
  38. }
  39. }
  40. // 定义notify方法,通知更新
  41. notify () {
  42. // 调用每个订阅者的update方法实现更新
  43. // stabilize the subscriber list first
  44. const subs = this.subs.slice()
  45. for (let i = 0, l = subs.length; i < l; i++) {
  46. subs[i].update()
  47. }
  48. }
  49. }
  50. // Dep.target用来存放目前正在评估的watcher
  51. // 全局唯一,并且一次也只能有一个watcher被评估
  52. // the current target watcher being evaluated.
  53. // this is globally unique because there could be only one
  54. // watcher being evaluated at any time.
  55. Dep.target = null
  56. // targetStack用来存放watcher栈
  57. const targetStack = []
  58. // 定义并导出pushTarget函数,接受Watcher类型的参数
  59. export function pushTarget (_target: ?Watcher) {
  60. // 入栈并将当前watcher赋值给Dep.target
  61. if (Dep.target) targetStack.push(Dep.target)
  62. Dep.target = _target
  63. }
  64. // 定义并导出popTarget函数
  65. export function popTarget () {
  66. // 出栈操作
  67. Dep.target = targetStack.pop()
  68. }

Watcher


  1. let uid = 0
  2. // watcher用来解析表达式,收集依赖对象,并在表达式的值变动时执行回调函数
  3. // 全局的$watch()方法和指令都以同样方式实现
  4. /**
  5. * A watcher parses an expression, collects dependencies,
  6. * and fires callback when the expression value changes.
  7. * This is used for both the $watch() api and directives.
  8. */
  9. // 定义并导出Watcher类
  10. export default class Watcher {
  11. // 定义变量
  12. vm: Component; // 实例
  13. expression: string; // 表达式
  14. cb: Function; // 回调函数
  15. id: number; // watcher实例Id
  16. deep: boolean; // 是否深层依赖
  17. user: boolean; // 是否用户定义
  18. computed: boolean; // 是否计算属性
  19. sync: boolean; // 是否同步
  20. dirty: boolean; // 是否为脏监视器
  21. active: boolean; // 是否激活中
  22. dep: Dep; // 依赖对象
  23. deps: Array<Dep>; // 依赖对象数组
  24. newDeps: Array<Dep>; // 新依赖对象数组
  25. depIds: SimpleSet; // 依赖id集合
  26. newDepIds: SimpleSet; // 新依赖id集合
  27. before: ?Function; // 先行调用函数
  28. getter: Function; // 指定getter
  29. value: any; // 观察值
  30. // 定义构造函数
  31. // 接收vue实例,表达式对象,回调函数,配置对象,是否渲染监视器5个参数
  32. constructor (
  33. vm: Component,
  34. expOrFn: string | Function,
  35. cb: Function,
  36. options?: ?Object,
  37. isRenderWatcher?: boolean
  38. ) {
  39. // 下面是对实例属性的赋值
  40. this.vm = vm
  41. // 如果是渲染监视器则将它赋值给实例的_watcher属性
  42. if (isRenderWatcher) {
  43. vm._watcher = this
  44. }
  45. // 添加到vm._watchers数组中
  46. vm._watchers.push(this)
  47. // 如果配置对象存在,初始化一些配置属性
  48. // options
  49. if (options) {
  50. this.deep = !!options.deep
  51. this.user = !!options.user
  52. this.computed = !!options.computed
  53. this.sync = !!options.sync
  54. this.before = options.before
  55. } else {
  56. // 否则将配属性设为false
  57. this.deep = this.user = this.computed = this.sync = false
  58. }
  59. this.cb = cb
  60. this.id = ++uid // uid for batching
  61. this.active = true
  62. this.dirty = this.computed // for computed watchers
  63. this.deps = []
  64. this.newDeps = []
  65. this.depIds = new Set()
  66. this.newDepIds = new Set()
  67. this.expression = process.env.NODE_ENV !== 'production'
  68. ? expOrFn.toString()
  69. : ''
  70. // 设置监视器的getter方法
  71. // parse expression for getter
  72. // 如果传入的expOrFn参数是函数直接赋值给getter属性
  73. if (typeof expOrFn === 'function') {
  74. this.getter = expOrFn
  75. } else {
  76. // 否则解析传入的表达式的路径,返回最后一级数据对象
  77. // 这里是支持使用点符号获取属性的表达式来获取嵌套需观测数据
  78. this.getter = parsePath(expOrFn)
  79. // 不存在getter则设置空函数
  80. if (!this.getter) {
  81. this.getter = function () {}
  82. process.env.NODE_ENV !== 'production' && warn(
  83. `Failed watching path: "${expOrFn}" ` +
  84. 'Watcher only accepts simple dot-delimited paths. ' +
  85. 'For full control, use a function instead.',
  86. vm
  87. )
  88. }
  89. }
  90. // 如果是计算属性,创建dep属性
  91. if (this.computed) {
  92. this.value = undefined
  93. //
  94. this.dep = new Dep()
  95. } else {
  96. // 负责调用get方法获取观测值
  97. this.value = this.get()
  98. }
  99. }
  100. // 评估getter,并重新收集依赖项
  101. /**
  102. * Evaluate the getter, and re-collect dependencies.
  103. */
  104. get () {
  105. // 将实例添加到watcher栈中
  106. pushTarget(this)
  107. let value
  108. const vm = this.vm
  109. // 尝试调用vm的getter方法
  110. try {
  111. value = this.getter.call(vm, vm)
  112. } catch (e) {
  113. // 捕捉到错误时,如果是用户定义的watcher则处理异常
  114. if (this.user) {
  115. handleError(e, vm, `getter for watcher "${this.expression}"`)
  116. } else {
  117. // 否则抛出异常
  118. throw e
  119. }
  120. } finally {
  121. // 最终执行“触摸”每个属性的操作,以便将它们全部跟踪为深度监视的依赖关系
  122. // "touch" every property so they are all tracked as
  123. // dependencies for deep watching
  124. if (this.deep) {
  125. // traverse方法递归每一个对象,将对象的每级属性收集为深度依赖项
  126. traverse(value)
  127. }
  128. // 执行出栈
  129. popTarget()
  130. // 调用实例cleanupDeps方法
  131. this.cleanupDeps()
  132. }
  133. // 返回观测数据
  134. return value
  135. }
  136. // 添加依赖
  137. /**
  138. * Add a dependency to this directive.
  139. */
  140. // 定义addDep方法,接收Dep类型依赖实例对象
  141. addDep (dep: Dep) {
  142. const id = dep.id
  143. // 如果不存在依赖,将新依赖对象id和对象添加进相应数组中
  144. if (!this.newDepIds.has(id)) {
  145. this.newDepIds.add(id)
  146. this.newDeps.push(dep)
  147. // 并在dep对象中添加监视器自身
  148. if (!this.depIds.has(id)) {
  149. dep.addSub(this)
  150. }
  151. }
  152. }
  153. // 清理依赖项集合
  154. /**
  155. * Clean up for dependency collection.
  156. */
  157. // 定义cleanupDeps方法
  158. cleanupDeps () {
  159. let i = this.deps.length
  160. // 遍历依赖列表
  161. while (i--) {
  162. const dep = this.deps[i]
  163. // 如果监视器的依赖对象列表中含有当前依赖对象
  164. // 从依赖对象中移除该监视器
  165. if (!this.newDepIds.has(dep.id)) {
  166. dep.removeSub(this)
  167. }
  168. }
  169. // 重置监视器的依赖相关属性,
  170. // 将新建立的依赖转换成常规依赖
  171. // 并清空新依赖列表
  172. let tmp = this.depIds
  173. this.depIds = this.newDepIds
  174. this.newDepIds = tmp
  175. this.newDepIds.clear()
  176. tmp = this.deps
  177. this.deps = this.newDeps
  178. this.newDeps = tmp
  179. this.newDeps.length = 0
  180. }
  181. // 订阅者接口,当依赖项变更时调用
  182. /**
  183. * Subscriber .
  184. * Will be called when a dependency changes.
  185. */
  186. // 定义update方法
  187. update () {
  188. // 如果是计算属性
  189. /* istanbul ignore else */
  190. if (this.computed) {
  191. // 计算属性的观察有两种模式:懒模式和立即模式
  192. // 默认都设置为懒模式,要使用立即模式需要至少有一个订阅者,
  193. // 典型情况下是另一个计算属性或渲染函数
  194. // A computed property watcher has two modes: lazy and activated.
  195. // It initializes as lazy by default, and only becomes activated when
  196. // it is depended on by at least one subscriber, which is typically
  197. // another computed property or a component's render function.
  198. // 当不存在依赖列表
  199. if (this.dep.subs.length === 0) {
  200. // 设置dirty属性为true,这是因为在懒模式下只在需要的时候才执行计算,
  201. // 所以为了稍后执行先把dirty属性设置成true,这样在属性被访问的时候
  202. // 才会执行真实的计算过程。
  203. // In lazy mode, we don't want to perform computations until necessary,
  204. // so we simply mark the watcher as dirty. The actual computation is
  205. // performed just-in-time in this.evaluate() when the computed property
  206. // is accessed.
  207. this.dirty = true
  208. } else {
  209. // 在立即执行模式中,需要主动执行计算
  210. // 但只在值真正变化的时候才通知订阅者
  211. // In activated mode, we want to proactively perform the computation
  212. // but only notify our subscribers when the value has indeed changed.
  213. // 调用getAndInvoke函数,判断是否观测值真正变化,并发布更新通知
  214. this.getAndInvoke(() => {
  215. this.dep.notify()
  216. })
  217. }
  218. } else if (this.sync) {
  219. // 如果同步执行,则调用实例run方法
  220. this.run()
  221. } else {
  222. // 否则将监视器添加进待评估队列
  223. queueWatcher(this)
  224. }
  225. }
  226. // 调度工作接口,会被调度器调用
  227. /**
  228. * Scheduler job interface.
  229. * Will be called by the scheduler.
  230. */
  231. // 定义run方法
  232. run () {
  233. // 如果当前监视器处于活跃状态,则立即调用getAndInvoke方法
  234. if (this.active) {
  235. this.getAndInvoke(this.cb)
  236. }
  237. }
  238. // 定义getAndInvoke方法,接收一个回调函数参数
  239. getAndInvoke (cb: Function) {
  240. // 获取新观测值
  241. const value = this.get()
  242. // 当旧值与新值不相等,或者挂厕纸是对象,或需要深度观察时
  243. // 触发变更,发布通知
  244. if (
  245. value !== this.value ||
  246. // 因为对象或数组即使相等时,其值可能发生变异所以也需要触发更新
  247. // Deep watchers and watchers on Object/Arrays should fire even
  248. // when the value is the same, because the value may
  249. // have mutated.
  250. isObject(value) ||
  251. this.deep
  252. ) {
  253. // 更细观测值,并设置置否dirty属性
  254. // set new value
  255. const oldValue = this.value
  256. this.value = value
  257. this.dirty = false
  258. // 如果是用户自定义监视器,则在调用回调函数时设置错误捕捉
  259. if (this.user) {
  260. try {
  261. cb.call(this.vm, value, oldValue)
  262. } catch (e) {
  263. handleError(e, this.vm, `callback for watcher "${this.expression}"`)
  264. }
  265. } else {
  266. cb.call(this.vm, value, oldValue)
  267. }
  268. }
  269. }
  270. // 评估和返回观测值方法,只在计算属性时被调用
  271. /**
  272. * Evaluate and return the value of the watcher.
  273. * This only gets called for computed property watchers.
  274. */
  275. // 定义evaluate方法
  276. evaluate () {
  277. // 如果是计算属性,获取观测是,并返回
  278. if (this.dirty) {
  279. this.value = this.get()
  280. this.dirty = false
  281. }
  282. return this.value
  283. }
  284. // 建立监视器的依赖方法,只在计算属性调用
  285. /**
  286. * Depend on this watcher. Only for computed property watchers.
  287. */
  288. // 定义depend方法
  289. depend () {
  290. // 如果依赖对象存在且存在当前运行监视器,建立依赖
  291. if (this.dep && Dep.target) {
  292. this.dep.depend()
  293. }
  294. }
  295. // 销毁监视器方法,将自身从依赖数组中移除
  296. /**
  297. * Remove self from all dependencies' subscriber list.
  298. */
  299. // 定义teardown方法
  300. teardown () {
  301. // 当监视器处于活跃状态,执行销毁
  302. if (this.active) {
  303. // 从监视器列表中移除监视器是高开销操作
  304. // 所以如果实例正在销毁中则跳过销毁
  305. // remove self from vm's watcher list
  306. // this is a somewhat expensive operation so we skip it
  307. // if the vm is being destroyed.
  308. // 当实例正常运行中,从监视器列表中移除监视器
  309. if (!this.vm._isBeingDestroyed) {
  310. remove(this.vm._watchers, this)
  311. }
  312. // 从所有依赖列表中移除该监视器
  313. let i = this.deps.length
  314. while (i--) {
  315. this.deps[i].removeSub(this)
  316. }
  317. // 将监视器的活跃状态置否
  318. this.active = false
  319. }
  320. }
  321. }

本篇主要是关于源码的解释,可以翻看观察系统的原理篇来对照理解。


在这里记录下了Vue的数据绑定具体实现的源代码的个人理解,有些细节的地方或许还认识的不够充分,观察系统里的三个类兜兜转转,关联性很强,在各自的方法中交叉地引用自身与其他类的实例,很容易让人头晕目眩,不管怎样,对于整体功能逻辑有清晰的认识,以后便能向更高层面迈进。

来源:https://segmentfault.com/a/1190000016898606

Vue源码探究-数据绑定的实现的更多相关文章

  1. Vue源码探究-状态初始化

    Vue源码探究-状态初始化 Vue源码探究-源码文件组织 Vue源码探究-虚拟DOM的渲染 本篇代码位于vue/src/core/instance/state.js 继续随着核心类的初始化展开探索其他 ...

  2. Vue源码探究-虚拟DOM的渲染

    Vue源码探究-虚拟DOM的渲染 在虚拟节点的实现一篇中,除了知道了 VNode 类的实现之外,还简要地整理了一下DOM渲染的路径.在这一篇中,主要来分析一下两条路径的具体实现代码. 按照创建 Vue ...

  3. Vue源码探究-全局API

    Vue源码探究-全局API 本篇代码位于vue/src/core/global-api/ Vue暴露了一些全局API来强化功能开发,API的使用示例官网上都有说明,无需多言.这里主要来看一下全局API ...

  4. Vue源码探究-事件系统

    Vue源码探究-事件系统 本篇代码位于vue/src/core/instance/events.js 紧跟着生命周期之后的就是继续初始化事件相关的属性和方法.整个事件系统的代码相对其他模块来说非常简短 ...

  5. Vue源码探究-源码文件组织

    Vue源码探究-源码文件组织 源码探究基于最新开发分支,当前发布版本为v2.5.17-beta.0 Vue 2.0版本的大整改不仅在于使用功能上的优化和调整,整个代码库也发生了天翻地覆的重组.可见随着 ...

  6. vue源码分析—数据绑定

    放进沙里附件拉萨就发牢骚:剑飞:啊撒

  7. VUE 源码学习01 源码入口

    VUE[version:2.4.1] Vue项目做了不少,最近在学习设计模式与Vue源码,记录一下自己的脚印!共勉!注:此处源码学习方式为先了解其大模块,从宏观再去到微观学习,以免一开始就研究细节然后 ...

  8. 大白话Vue源码系列(03):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

  9. 大白话Vue源码系列(04):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

随机推荐

  1. ES6之主要知识点(五)函数

    函数参数的默认值 作用域 ; function f(x, y = x) { console.log(y); } f() let x = ; function f(y = x) { let x = ; ...

  2. css3之文本text-overflow 与 word-wrap, word-break

    CSS3 Text Overflow属性 CSS3文本溢出属性指定应向用户如何显示溢出内容 语法: text-overflow:clip | ellipsis 但是text-overflow只是用来说 ...

  3. InceptionV3代码解析

    InceptionV3代码解析 参考博文:https://blog.csdn.net/superman_xxx/article/details/65451916 读了Google的GoogleNet以 ...

  4. vue 生产环境和测试环境的配置

    我们引用的是axios 给src目录增加 api 文件夹 里面写上index.js // 配置API接口地址 var root = process.env.API_ROOT // 引用axios va ...

  5. 简单的sequence unpacking

    t = (1, 2, ‘hl’) x, y, z = t 上述方法可用于任何sequence

  6. CODE[VS]4633:Mz树链剖分练习

    Description 给定一棵结点数为n的树,初始点权均为0,有依次q个操作,每次操作有三个参数a,b,c,当a=1时,表示给b号结点到c号结点路径上的所有点(包括b,c,下同)权值都增加1,当a= ...

  7. RuntimeError: You called this URL via POST, but the URL doesn’t end in a slash and you have APPEND_SLASH set.

    做公众号测试的时候,发现了个问题: 提交表单报错:RuntimeError: You called this URL via POST, but the URL doesn’t end in a sl ...

  8. 转:Linux 2.4.x内核软中断机制

    源地址:http://www.ibm.com/developerworks/cn/linux/kernel/interrupt/ Linux 2.4.x内核软中断机制 杨沙洲 (pubb@163.ne ...

  9. java代理概念

    代理的概念 动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的. 动态代理技术就是用来产生一个对象的代理对象的 ...

  10. poj2752

    poj2752找所有的前缀等于后缀,那就是找所有前缀等于后缀的前缀,递归再用栈存一下 #include<iostream> #include<cstdio> #include& ...