Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
xwjie opened this issue Jan 18, 2018 · 0 comments
Open

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

xwjie opened this issue Jan 18, 2018 · 0 comments

Comments

@xwjie
Copy link
Owner

xwjie commented Jan 18, 2018

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

关键点的要理解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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant