vue 双向数据绑定

class myVue {
  constructor(options) {
    初始数据(options是实例传的数据)
    this._init(options)
  }

  _init(options) {
    this.$options = options
    this.$el = document.querySelector(options.el)
    this.$data = options.data
    this.$methods = options.methods

    this._watchers = {}
    this._proxy(this, '$data') //this为vue实例
    this._observer(this.$data)
    this._complie(this.$el)
  }
  //代理
  _proxy(vm, strKey) {
    Object.keys(vm[strKey]).map((key) => {
      Object.defineProperty(vm, key, {
        enumerable: true,
        configurable: true,
        get() {
          console.log(vm[strKey][key])
          return vm[strKey][key]
        },
        set(val) {
          console.log(vm[strKey][key], val)
          vm[strKey][key] = val
        }
      })
    })
  }
  //劫持
  _observer(obj, objKey) {
    for (const key in obj) {
      let value
      if (obj.hasOwnProperty(key)) {
        let watchers
        if(objKey) {
          watchers = this._watchers[objKey][key] = []
        } else {
          watchers = this._watchers[key] = []
        }

        value = obj[key]

        if(typeof value === 'object') {
          this._observer(value, key);
        }
        
        Object.defineProperty(obj, key, {
          enumerable: true,
          configurable: true,
          get() {
            console.log(`获取${value}`)
            return value
          },
          set(newVal) {
            console.log(`更新${value}`)
            if (value !== newVal) {
              value = newVal;
              //通知watcher
              watchers.forEach(function (item) {
                item.update();
              })
            }
          }
        })
      }
    }
  }
  //解析指令
  _complie(root) {
    let nodes = root.children

    for(let i = 0, l = nodes.length; i < l; i++) {
      let childNodes = nodes[i]
      if(childNodes.length) {
        this._complie(childNodes)
      }

      if(childNodes.hasAttribute('v-click')) {
        let attrVal = childNodes.getAttribute('v-click')
        childNodes.onclick = getExpArr(this.$methods, attrVal).bind(this)
      }

      if(childNodes.hasAttribute('v-model') && (childNodes.tagName == 'INPUT' || childNodes.tagName == 'TEXTAREA')) {
        let attrVal = childNodes.getAttribute('v-model')
        childNodes.addEventListener('input',(() => {
          getExpArr(this._watchers, attrVal).push(new Watcher(
            'input',
            childNodes,
            this,
            attrVal,
            'value'
          ))
          return () => {
            getExpArr(this, attrVal) = childNodes.value
          }
        })())
      }

      if(childNodes.hasAttribute('v-text')) {
        let attrVal = childNodes.getAttribute('v-text')
        getExpArr(this._watchers, attrVal).push(new Watcher(
          'text',
          childNodes,
          this,
          attrVal,
          'innerHTML'
        ))
      }
    }
  }
}
//将obj['a.b']变为obj['a']['b']
function getExpArr(obj, attrVal) {
  return attrVal.split('.').reduce((pre, cur) => {
    return pre[cur]
  }, obj)
}

class Watcher {
  constructor(name, el, vm, expression, attr) {
    this.name = name;         //指令名称,例如文本节点,该值设为"text"
    this.el = el;             //指令对应的DOM元素
    this.vm = vm;             //指令所属myVue实例
    this.expression = expression;           //指令对应的值,本例如"number"
    this.attr = attr;         //绑定的属性值,本例为"innerHTML"

    this.update()
  }
  //更新
  update() {
    let val = getExpArr(this.vm, this.expression)
    this.el[this.attr] = this.name === 'text' ? JSON.stringify(val) : val
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值