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


    /* @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) } }
  • 相关阅读:
    由DataSet导出生成excel的几种方法
    linq日期查询
    上传文件
    C#获取文件的MD5值
    设置图层透明度
    highcharts中的x轴如何显示时分秒时间格式
    axios中文文档
    如何在 Highcharts 图中当所占百分比为 0 时不显示0%
    一个简易的选择小时(时分秒)的插件
    三分钟上手Highcharts简易甘特图
  • 原文地址:https://www.cnblogs.com/dhsz/p/7064870.html
Copyright © 2020-2023  润新知