解析vue响应式原理3

import {
warn,
remove,
isObject,
parsePath,
_Set as Set,
handleError,
noop
} from ‘…/util/index’

import { traverse } from ‘./traverse’
import { queueWatcher } from ‘./scheduler’
import Dep, { pushTarget, popTarget } from ‘./dep’

import type { SimpleSet } 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.
    */
    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;
    newDeps: Array;
    depIds: SimpleSet;
    newDepIds: SimpleSet;
    before: ?Function;
    getter: Function;
    value: any;

constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {
vm._watcher = this
}
vm._watchers.push(this)
// options
if (options) {
this.deep = !!options.deep
this.user = !!options.user
this.lazy = !!options.lazy
this.sync = !!options.sync
this.before = options.before
} 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 = noop
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 () {
    pushTarget(this)
    let value
    const vm = this.vm
    try {
    value = this.getter.call(vm, vm)
    } catch (e) {
    if (this.user) {
    handleError(e, vm, getter for watcher "${this.expression}")
    } else {
    throw e
    }
    } finally {
    // “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.
    */
    evaluate () {
    this.value = this.get()
    this.dirty = false
    }

/**

  • Depend on all deps collected by this watcher.
    */
    depend () {
    let i = this.deps.length
    while (i–) {
    this.deps[i].depend()
    }
    }
    Absorbing material: www.goodsmaterial.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值