vue2双绑原理

采用数据劫持结合发布-订阅者模式,通过object.defineProperty劫持各个属性的gettert和setter,在数据变动时发布消息给订阅者,触发响应的监听回调,主要分为以下几个步骤:

1.需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上getter和setter

2.通过compile解析模板指令({{xxx}}、v-model等),将模板中的变量替换成数据,并对每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据变动,收到通知,更新视图

3.watcher订阅者是observer和compile之间通信的桥梁

①在自身实例化时,往dep(属性订阅器)中添加自己

②自身必须有一个update方法

③待属性变动,dep.notify()通知时,能调用自身的update方法,触发compile中绑定的回调

4.MVVM作为数据绑定的入口,整合observer、compile和watcher三者,通过observer来监听自己的model数据变化,通过compile来解析编译模板指令,最终利用watcher搭起observer和compile中间的通信桥梁,达到数据变化->视图更新,视图交互变化(input)->数据model变更的双向绑定效果。

class Vue {
  constructor(obj_instance) {
    this.$data = obj_instance.data
    Observer(this.$data)
    Compile(obj_instance.el, this)
  }
}

function Observer(data_instance) {
  if (!data_instance || typeof data_instance !== 'object') return
  const dependency = new Dependency()
  Object.keys(data_instance).forEach(key => {
    let value = data_instance[key]
    // 递归监听子属性对象
    Observer(value)
    Object.defineProperty(data_instance, key, {
      enumerable: true,
      configurable: true,
      get() {
        console.log(`访问了属性${key} -> 值:${value}`)
        // get时添加watcher到dep的subs中
        Dependency.temp && dependency.addSub(Dependency.temp)
        return value
      },
      set(newValue) {
        console.log(`属性${key}的值:${value}修改为 => ${newValue}`)
        value = newValue
        // 若将子属性的值修改为对象,也要监听
        Observer(value)
        // set时触发dep.nitify,批量更新
        dependency.notify()
      }
    })
  })
}

function Compile(element, vm) {
  vm.$el = document.querySelector(element)
  // 创建文档碎片,将所有子节点添加进fragment,统一修改后再appendChild到el上
  const fragment = document.createDocumentFragment()
  let child
  while (child = vm.$el.firstChild) {
    fragment.append(child)
  }
  fragment_compile(fragment)
  function fragment_compile(node) {
    const pattern = /\{\{\s*(\S+)\s*\}\}/
    if (node.nodeType === 3) {
      // 对于文本节点,匹配{{xxx}},并将xxx替换成数据
      const nodeValue = node.nodeValue
      const result = pattern.exec(node.nodeValue)
      if (result) {
        const arr = result[1].split('.')
        const value = arr.reduce((total, current) => { console.log(total, current); return total[current] }, vm.$data)
        node.nodeValue = nodeValue.replace(pattern, value)
        // 添加监听者,数据变动,更新视图
        new Watcher(vm, result[1], newValue => {
          node.nodeValue = nodeValue.replace(pattern, newValue)
        })
      }
      return
    } if (node.nodeType === 1 && node.nodeName === 'INPUT') {
      // 对于input节点,判断属性上是否存在v-model,若存在,绑定input事件,修改$data中对应属性的值
      const attr = Array.from(node.attributes)
      attr.forEach(i => {
        if (i.nodeName === 'v-model') {
          const value = i.nodeValue.split('.').reduce((total, current) => total[current], vm.$data)
          node.value = value
          // 添加监听者,数据变动,更新视图
          new Watcher(vm, i.nodeValue, newValue => {
            node.value = newValue
          })
          node.addEventListener('input', e => {
            const arr1 = i.nodeValue.split('.')
            const arr2 = arr1.slice(0, arr1.length - 1)
            const final = arr2.reduce((total, current) => total[current], vm.$data)
            final[arr1[arr1.length - 1]] = e.target.value
          })
        }
      })
    }
    node.childNodes.forEach(child => fragment_compile(child))
  }
  vm.$el.appendChild(fragment)
}
// 依赖 - 收集和通知订阅者
class Dependency {
  constructor() {
    this.subscribers = []
  }
  addSub(sub) {
    this.subscribers.push(sub)
  }
  notify() {
    this.subscribers.forEach(sub => sub.update())
  }
}

// 订阅者
class Watcher {
  constructor(vm, key, callback) {
    this.vm = vm
    this.key = key
    this.callback = callback
    // 临时属性,触发getter
    Dependency.temp = this
    key.split('.').reduce((total, current) => total[current], vm.$data)
    Dependency.temp = null
  }
  update() {
    const value = this.key.split('.').reduce((total, current) => total[current], this.vm.$data)
    this.callback(value)
  }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Neo 丶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值