您的位置:首页 > Web前端 > Vue.js

vue.js 源代码学习笔记 ----- $watcher

2017-06-22 14:39 543 查看
/* @flow */

import { queueWatcher } from './scheduler'
import Dep, { pushTarget, popTarget } from './dep'

import {
warn,
remove,
isObject,
parsePath,
_Set as Set,
handleError
} from '../util/index'

let uid = 0

/**
* A watcher parses an expression, collects dependencies,
* and fires callback when the expression value changes.
* This is used for both the $watch() api and directives.

一个 watcher编译成 一个表达式 和 依赖集合, 还能出发回调当 v-model的值改变
这也能用到wathcer api中, 如果 vm.$watch('abc', fn); */
export default class Watcher {
vm: Component; expression: string; cb: Function; id: number; deep: boolean; user: boolean; lazy: boolean; sync: boolean; dirty: boolean; active: boolean; deps: Array<Dep>; newDeps: Array<Dep>; depIds: Set; newDepIds: Set; getter: Function; value: any; constructor ( vm: Component, expOrFn: string | Function, cb: Function, options?: Object ) {
//这个watcher所属的vm this.vm = vm

// 一个vm 有多个watchers , vm.$watch(a,fn1) vm.$watch(b,fn2) {{b.UpperCase()}} 等等 vm._watchers.push(this)
// options if (options) {
//是否深度检测依赖 this.deep = !!options.deep this.user = !!options.user this.lazy = !!options.lazy this.sync = !!options.sync } else { this.deep = this.user = this.lazy = this.sync = false }
this.cb = cb this.id = ++uid // uid for batching this.active = true this.dirty = this.lazy // for lazy watchers this.deps = [] this.newDeps = [] this.depIds = new Set() this.newDepIds = new Set() this.expression = process.env.NODE_ENV !== 'production' ? expOrFn.toString() : '' // parse expression for getter if (typeof expOrFn === 'function') {
//监控函数 this.getter = expOrFn } else {
//监控表达式, 注意 这里也会返回一个路径的函数 this.getter = parsePath(expOrFn)
if (!this.getter) {
this.getter = function () {}
process.env.NODE_ENV !== 'production' && warn( `Failed watching path: "${expOrFn}" ` + 'Watcher only accepts simple dot-delimited paths. ' + 'For full control, use a function instead.', vm ) } } this.value = this.lazy ? undefined : this.get() } /** * Evaluate the getter, and re-collect dependencies. */ get () {
//Dep.target = this pushTarget(this)
let value const vm = this.vm
if (this.user) { try {
// 因为传入了 vm, 所以在methods中可以取得this.data.abc
// 比如 this.getter 是 fucntion(){ return this.price * this.nums; }; 因为执行这个函数, 也就会去获取 this.price 和 this.nums的值
// 就会分别触发这两个数据的get方法, 然后这个watcher就会加入这两个数据的dep, this.push(depq) this.push(dep2); 通过这一步就收集了这个函数的两个依赖 value = this.getter.call(vm, vm) } catch (e) { handleError(e, vm, `getter for watcher "${this.expression}"`) } } else { value = this.getter.call(vm, vm) }
// "touch" every property so they are all tracked as // dependencies for deep watching
//“触摸”每一个属性,这样它们都被跟踪为 深度观察依赖
if (this.deep) {
traverse(value)
}
popTarget()
this.cleanupDeps() return value
} /** * 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) } } } /** * Clean up for dependency collection. */ cleanupDeps () {
let i = this.deps.length while (i--) { const dep = this.deps[i] if (!this.newDepIds.has(dep.id)) { dep.removeSub(this) } }
let tmp = this.depIds this.depIds = this.newDepIds this.newDepIds = tmp this.newDepIds.clear() tmp = this.deps this.deps = this.newDeps this.newDeps = tmp this.newDeps.length = 0 } /** * Subscriber interface. * Will be called when a dependency changes. */ update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) } } /** * Scheduler job interface. * Will be called by the scheduler. */ run () { if (this.active) { const value = this.get() if ( value !== this.value || // Deep watchers and watchers on Object/Arrays should fire even // when the value is the same, because the value may // have mutated. isObject(value) || this.deep ) { // set new value const oldValue = this.value this.value = value if (this.user) { try { this.cb.call(this.vm, value, oldValue) } catch (e) { handleError(e, this.vm, `callback for watcher "${this.expression}"`) } } else { this.cb.call(this.vm, value, oldValue) } } } } /** * Evaluate the value of the watcher. * This only gets called for lazy watchers.

计算一个watcher的值, 这个只会被 延迟watchers调用 */ evaluate () { this.value = this.get() this.dirty = false } /** * Depend on all deps collected by this watcher.
通过这个watcher, 收集所有的依赖? */ depend () { let i = this.deps.length while (i--) { this.deps[i].depend() } } /** * Remove self from all dependencies' subscriber list.

从所有的依赖订阅清单中移除自己 */ teardown () { if (this.active) { // remove self from vm's watcher list // this is a somewhat expensive operation so we skip it // if the vm is being destroyed.
// 移除依赖是一个昂贵的操作, 所有如果这个vm已经销毁le, 我们就跳一跳
if (!this.vm._isBeingDestroyed) { remove(this.vm._watchers, this) }
let i = this.deps.length
while (i--) { this.deps[i].removeSub(this) }
this.active = false } } } /** * Recursively traverse an object to evoke all converted * getters, so that every nested property inside the object * is collected as a "deep" dependency.

递归追踪一个对象去唤起所有的已经转换的getters,
这样在一个对象的所以嵌套的熟悉都被收集成为一个深层的依赖 */

const seenObjects = new Set()
function traverse (val: any) { seenObjects.clear() _traverse(val, seenObjects) } function _traverse (val: any, seen: Set) { let i, keys const isA = Array.isArray(val) if ((!isA && !isObject(val)) || !Object.isExtensible(val)) { return }
if (val.__ob__) { const depId = val.__ob__.dep.id
if (seen.has(depId)) { return }
seen.add(depId) }
if (isA) { i = val.length while (i--) _traverse(val[i], seen) } else {
//for in 都不用了, 加快速度 keys = Object.keys(val) i = keys.length while (i--) _traverse(val[keys[i]], seen) } }

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: