Ethan Zhu
©️朱俊辉
All Rights Reserved.

简单实现vue的数据双向绑定

目前接触的框架中,angular和vue中都会有双向绑定,angular是通过脏值来实现的,而vue的实现是通过getter和setter来实现的,今天在微信公众号:前端大学,看到了一篇文章,我就顺着他的思路实现了一波

首先得了解两块知识

  1. Object.defineProperty(前一篇文章有说明)
  2. 发布订阅模式

发布订阅模式是设计模式中比较常见的一种,其中有两个角色:发布者和订阅者。多个订阅者可以向同一发布者订阅一个事件,当事件发生的时候,发布者通知所有订阅该事件的订阅者。

    class Observer {
      constructor (data) {
        // 如果不是对象则返回
        if (!data || typeof data !== 'object') {
          return
        }
        this.data = data
        this.walk()
      }

      // 对传入的数据进行数据劫持
      walk () {
        for (let key in this.data) {
          this.defineReactive(this.data, key, this.data[key])
        }
      }

      // 创建当前属性的一个发布实例,使用Object.defineProperty来对数据进行劫持
      defineReactive(obj, key, val) {
        // 创建一个发布者
        const dep = new Dep()

        new Observer(val)
        // 定义name的getter和setter,
        Object.defineProperty(obj, key, {
          get () {
            if (Dep.target) {
              dep.addSub(Dep.target)
            }
            return val
          },
          set (newVal) {
            if (val === newVal) {
              return
            }
            val = newVal
            new Observer(newVal)
            dep.notify()
          }
        })
      }
    }

    // 发布者,将依赖该属性的watcher都加入subs数组,当该属性改变的时候,则调用所有依赖该属性的watcher的更新函数,触发更新
    class Dep {
      constructor() {
        this.subs = []
      }

      addSub(sub) {
        if (this.subs.indexOf(sub) < 0) {
          this.subs.push(sub)
        }
      }
      // 发布方法,去掉用观察者的更新方法
      notify() {
        this.subs.forEach((sub) => {
          sub.update()
        })
      }
    }
    Dep.target = null


    // 观察者
    class Watcher {
      constructor(vm, keys, updateCb) {
        this.vm = vm
        this.keys = keys
        this.updateCb = updateCb
        this.value = null
        this.get()
      }

      get() {
        Dep.target = this
        const keys = this.keys.split('.')
        let value = this.vm
        keys.forEach(_key => {
          value = value[_key]
        })
        this.value = value
        Dep.target = null
        return this.value
      }

      update() {
        const oldValue = this.value
        const newValue = this.get()
        if (oldValue !== newValue) {
          this.updateCb(oldValue, newValue)
        }
      }
    }


    let data = {
      name: 'zzh'
    }
    debugger
    new Observer(data)

    // 监听data对象的name属性,当data.name发生变化时,触发updateCb函数
    new Watcher(data, 'name', (oldValue, newValue) => {
      console.log(oldValue, newValue)
    })

    data.name = 'ethan zhu'

这里主要使用了Object.defineproperty和发布订阅模式实现的,当然这里只是个简单的demo,真正的vue源码会更加复杂,因为还参杂了其他的逻辑

2018-08-31

发表评论