欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Object.defineProperty实现双向数据绑定

程序员文章站 2022-07-12 21:51:18
...

数据劫持:使用Object.definePropert方法在set函数中通知Watcher是否需要更新视图
github:https://github.com/qiangf811/SelfVue

Observer

const Dep = require('./dep')

class Observer {
  constructor (data) {
    this.defineData(data)
  }
  defineData (obj) {
    if (!obj || typeof obj !== 'object') {
      return
    }
    Object.keys(obj).forEach(key => {
      this.defineReactive(obj, key, obj[key])
    })
  }
  defineReactive (obj, key, val) {
    this.defineData(val)
    let dep = new Dep()
    Object.defineProperty(obj, key, {
      configurable: true,
      enumerable: true,
      set: newVal => {
        if (val !== newVal) {
          console.log(key + '改变了', val + '变成了' + newVal)
          val = newVal
          dep.notify()
        }
      },
      get: () => {
        if (Dep.target) {
          dep.addSub(Dep.target)
        }
        return val
      }
    })
  }
}

module.exports = function (data) {
  return new Observer(data)
}

watcher

const Dep = require('./dep')

class Watcher {
  constructor (vm, exp, cb) {
    this.cb = cb
    this.vm = vm
    this.exp = exp
    this.value = this.get()
  }
  update () {
    this.run()
  }
  run () {
    let value = this.vm.data[this.exp]
    let oldVal = this.value
    if (value !== oldVal) {
      this.value = value
      this.cb.call(this.vm, value, oldVal)
    }
  }
  get () {
    Dep.target = this
    // 缓存自己
    var value = this.vm.data[this.exp] // 强制执行监听器里的get函数
    Dep.target = null // 释放自己
    return value
  }
}
module.exports = function (vm, exp, cb) {
  return new Watcher(vm, exp, cb)
}

Dep

module.exports = class Dep {
  constructor () {
    this.subs = []
  }
  /**
   * 添加订阅者
   * @param {Watcher} sub 订阅者实例
   */
  addSub (sub) {
    this.subs.push(sub)
  }
  /**
   * 通知所有订阅者
   * @return {Null}
   */
  notify () {
    this.subs.forEach(sub => {
      sub.update()
    })
  }
}

MyVue

const Observer = require('./observer')
const Watcher = require('./watcher')
module.exports = class SelfVue {
  constructor (data, el, exp) {
    let self = this
    self.data = data
    Object.keys(data).forEach(key => {
      self.proxyKeys(key)
    })
    Observer(data) // 构造数据监视器
    el.innerHTML = self.data[exp] // 初始化模板数据的值
    Watcher(self, exp, value => { // 添加订阅者,监听具体字段
      el.innerHTML = value
    })
    return self
  }
  proxyKeys (key) {
    let self = this
    Object.defineProperty(this, key, {
      configurable: true,
      enumerable: true,
      get: () => {
        return self.data[key]
      },
      set: (newVal) => {
        self.data[key] = newVal
      }
    })
  }
}