实现自己的vue(2)(响应式)

请结合上一期观看

1.创建Obsever,给属性添加defineProperty,

// 给每个属性添加响应式
class Obsever {
  constructor(vm) {
    this.vm = vm
    this.data = vm.$data
    this.obsever()
  }
  obsever () {
    const dep = new Dep()
    for (let key in this.data) {
      let val = this.data[key]
      Object.defineProperty(this.data, key, {
        get () {
          Dep.target && dep.addSub(Dep.target)
          return val
        },
        set (newVal) {
          val = newVal
          //触发相应的更新回调
          dep.notify()
        }
      })
    }
  }
}

2.创建Watcher类

作用:在之后数据变化时,不直接去通知依赖更新,而是通知依赖对应的Watch实例,由Watcher实例去通知真正的视图,可以理解成watch类就代表这个依赖。

PS:谁用到了数据,谁就是依赖,我们就为谁创建一个Watcher实例,在创建Watcher实例的过程中会自动的把自己添加到这个数据对应的依赖管理器中,以后这个Watcher实例就代表这个依赖,当数据变化时,我们就通知Watcher实例,由Watcher实例再去通知真正的依赖。

当数据变化时,会触发数据的setter,在setter中调用了dep.notify()方法,在dep.notify()方法中,遍历所有依赖(即watcher实例),执行依赖的update()方法,也就是Watcher类中的update()实例方法,在update()方法中调用数据变化的更新回调函数,从而更新视图。

class Watcher {
  constructor(vm, key, cb) {
      this.vm = vm
      this.cb = cb
      this.oldVaL = this.getOldVal(key, vm)
  }
  getOldVal(key, vm) {
      Dep.target = this
      const oldVal = vm.$data[key]
      Dep.target = null
      return oldVal
  }
  update() {
      this.cb()
  }
}

3.创建Dep类(依赖管理器)

在getter中收集依赖,在setter中通知依赖更新。

class Dep {
  constructor() {
    this.subList = []
  }
  //添加监听对象
  addSub (watcher) {
    this.subList.push(watcher)
  }
  //执行对象响应回调
  notify () {
    this.subList.forEach((sub) => {
      sub.update()
    })
  }
}

4.在我上期作品的Compile类里添加以下代码

  //文本节点
  compileText (node) {
    const con = node.textContent
    // 解析出{{}},变量赋值
    const reg = /\{\{(.+?)\}\}/g
    if (reg.test(con)) {
      const newVal = con.replace(reg, (...arg) => {
        // arg[1]就是data中的变量名,此处为msg、info
        new Watcher(this.vm, arg[1], () => {
            //更新页面,会在defineProperty的set方法调用此回调
          const xx = con.replace(reg, (...arg) => {
            return this.vm.$data[arg[1]]
          })
          node.textContent = xx
        })
        return this.vm.$data[arg[1]]
      })
      node.textContent = newVal
    }
  }

5.点击就可以实现实时更新

处理v-model

 //元素节点
  compileElement (node) {
    //获取标签的属性
    const attrs = node.attributes
    //如果属性长度>0
    if (!!attrs.length) {
      //将attrs类数组转化为真正的数组
      const attrsArr = Array.from(attrs)
      attrsArr.forEach((att) => {
        // 如果有v-model属性就讲属性的值赋值给data里面的值
        if (att.nodeName === 'v-model') {
          const val = att.value
          node.value = this.vm.$data[val]
          if (node.tagName.toLowerCase() === 'input') {
            // 在这里处理input事件  
            node.addEventListener('input', (event) => {
              this.vm.$data[val] = event.target.value
            });
          }
        }
      })
    }
  }

效果图

object侦测总结:
1.Data通过observer转换成了getter/setter的形式来追踪变化。
当外界通过Watcher读取数据时,会触发getter从而将Watcher添加到依赖(Dep)中。
2.当数据发生了变化时,会触发setter,从而向Dep中的依赖(即Watcher)发送通知。
3.Watcher接收到通知后,会向外界发送通知,变化通知到外界后可能会触发视图更新,也有可能触发用户的某个回调函数等。

如有不足,还请多多指教!

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何时复西归

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值