Vue数据双向绑定原理

1、数据绑定渲染过程

  • 创建实例
new Vue({...})
  • 为实例中赋值el、data、methods等
this.$data = data
  • 开启observer,监听data
Object.defineProperty()
  • observer中为每一个observer中的监听属性生成对应订阅器
new Dep()
  • 在get中等待订阅者加入,set中等待数据改变并通知订阅器更新视图
  • 开启complier,创建文档碎片,将el整个劫持减少dom操作消耗
const $el = document.querySelectorAll(el)[0]
const fragment = document.createDocumentFragment()
fragement.appendChild($el)
  • 遍历fragment所有节点属性找到指令属性以及特殊符号(如{{}})生成对应订阅者
[].slice.fragment.forEach(val => {
    let attributes = val.attributes
    [].slice.attributes.forEach(attr => {
        // 获取所有节点属性,进行正则、判断等操作
        ....
        // 生成对应订阅者传入参数
        new Watcher(...)
    })
})
  • Watcher通知订阅器将自身加入(Watcher即订阅者,上面说到observer中get等待订阅者加入,可通过触发get属性)
// 通过访问触发订阅器对应get,将自身赋值给target供observer中get添加自身
target = this
let value = $vm.data[attr]
target = null
  • Watcher须有一个update方法更新该订阅器对应的视图
Class Watcher{
    constructor () {
        ...
    }
    update () {
        ...
    }
}
  • observer在get中收到则将订阅者添加进自身订阅器中
const dep = new Dep()
let value = obj[key]
Object.defineProperty(obj, key, {
    get: () => {
        target && dep.addSub(target)
    },
    set: (newVal) => {
        if (newVal !== val) {
            val = newVal
            // 数据变动通知更新视图
            dep.notify(newVal)
        }
    }
})
  • 当数据变化时触发set,触发订阅器notify方法,调用该订阅器中每一个订阅者的update方法
class Dep{
    constructor () {
        this.sub = []
    }
    notify () {
        this.sub.forEach(val => {
            val.update()
        })
    }
    addSub (target) {
        this.sub.push(target)
    }
}
  • 触发watcher中的update替换数据,完成视图渲染

总结

初始化时监听data中的属性,为每个属性添加dep,运用闭包的原理等待watcher,之后开始编译模板,生成每个特殊指令对应的Watcher,通过触发对应data中的get将Watcher添加进去,当数据发生变化时通知该数据对应的每一个watcher,触发他们的update方法,完成数据绑定

源码地址 https://github.com/walkjs/MVVM

demo预览 https://walkjs.github.io/MVVM/

你可能感兴趣的:(Vue数据双向绑定原理)