Skip to content

16 实现属性的观察(监听) #18

@xwjie

Description

@xwjie

官方文档里面有 计算属性和 侦听属性,容易让人误解,其实一个是形容词加名词,一个是动宾结构,完全不一样。

关键点的要理解watch的机制,如果理解了的话,其实实现起来很简单,代码量很少。

增加实例方法

  /**
   * 观察某个属性
   * 
   * 把属性的get方法拿出来,调用get的时候和会对应的新建的watch关联起来(注册了依赖关系)
   * 属性修改的时候,就会调用所有的watch,然后就会调用回调。
   * (这里的回调其实就是用户写的观察函数)
   * 
   * @param {*} key 
   * @param {*} cb 
   */
  $watchField(key: string, cb: Function) {
    const getter = Object.getOwnPropertyDescriptor(this, key).get
    this.$watch(getter, cb)
  }

  /**
   * 观察某个方法,调用这个方法之后,会执行callback
   *
   * @param {*} getter
   * @param {*} cb
   */
  $watch(getter: Function, cb: Function) {
    new Watcher(this, {
      getter,
      cb
    })

    // fixme
    //return function unwatchFn() {
    //  watcher.teardown()
    //}
  }

watch 类的简化代码

let uid = 0

export default class Watcher {

  vm: Xiao

  getter: Function

  depIds: SimpleSet

  cb: ?Function

  value: ?any

  _uid: number

  /**
   *
   * @param {*} vm
   * @param {*} option
   *  getter: 函数,为render函数或者属性的get函数
   *  cb : 回调函数,可以为空
   */
  constructor(vm: Xiao, option: Object) {
    this.vm = vm
    this._uid = ++uid

    this.getter = option.getter
    this.cb = option.cb

    this.depIds = new Set()

    log(`[Watcher${this._uid}] _INIT_`)

    this.get()
  }

  get() {
    try {
      pushTarget(this)
      const value = this.getter.call(this.vm, this.vm)

      // 监控属性的时候,回调不为空
      if (this.cb) {
        const oldValue = this.value

        if (value !== oldValue) {
          try {
            this.cb.call(this.vm, value, oldValue)
          } catch (error) {

          }
        }
      }

      // 保存最新值
      this.value = value
    }
    finally {
      popTarget()
    }
  }

  /**
  * Add a dependency to this directive.
  */
  addDep(dep: Dep) {
    if (!this.depIds.has(dep.id)) {
      dep.addSub(this)
      this.depIds.add(dep.id)
    }
  }

  update() {
    log(`[Watcher${this._uid}] update`)

    // fixme
    this.get();
  }
}

测试代码

<!DOCTYPE html>
<html>
<head>
	<title>Xiao框架之helloworld</title>
	<script src="../dist/xiao.js"></script>
</head>
<body>
<h1>watch测试</h1>
<div id="demo">{{ fullName }}</div>
<script>
var app = new Xiao({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val, oldvalue) {
      console.log('firstName', oldvalue, val);
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val, oldvalue) {
      console.log('lastName', oldvalue, val);
      this.fullName = this.firstName + ' ' + val
    }
  }
})

setTimeout(function(){
	app.firstName = 'xx';
	app.lastName = 'yy';
}, 2000);

</script>

</body>
</html>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions