美文网首页
Vue数据绑定原理

Vue数据绑定原理

作者: 琉璃_xin | 来源:发表于2019-10-11 17:35 被阅读0次

源码地址
VueJS双向数据绑定是通过对数据的劫持来实现的。核心就是Object.defineProperty(data, key, descriptor)

如何实现一个简单的vue数据双向绑定呢?

  1. 实现数据劫持,监听数据对象
function Mvue(options) {
    this.$options = options || {}
    let data = (this.$data = options.data)
    let vm = this

    //代理 data上属性代理到vm实例上
    _proxy(data, vm)

    //observe data
    //监测数据变化
    _observe(data, vm)
}

function _proxy(data, target) {
    let vm = target
    Object.keys(data).forEach(key => {
        Object.defineProperty(vm, key, {
            enumerable: true,
            configurable: false,
            get: function() {
                return vm.$data[key]
            },
            set: function(newVal) {
                vm.$data[key] = newVal
            }
        })
    })
}

// -----------------observe ------------------------

function _observe(data, vm) {
    if (!data || typeof data !== 'object') {
        return
    }
    return new Observer(data)
}

function Observer(data) {
    this.data = data
    this.walk(data)
}

Observer.prototype.walk = function(data) {
    const keys = Object.keys(data)
    for (let i = 0; i < keys.length; i++) {
        let key = keys[i]
        this.defineReactive(data, key, data[key])
    }
}

Observer.prototype.defineReactive = function(data, key, val) {
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: false,
        get: function() {
            return val
        },
        set: function(newVal) {
            val = newVal
            console.log(`this is change message: ${val}`)
        }
    })
}
<body>
  <div id="app">
    {{message}}
  </div>
  <script src="./index.js"></script>
  <script>
    var app = new Mvue({
      el: '#app',
      data: {
        message: 'hello Mvue'
      }
    });
  </script>
</body>

在控制台进行验证修改,已经可以获取数据并且监听数据的变化


image.png
  1. 消息订阅
    在监听数据变化的同时,需要去通知订阅者
function Mvue(options) {
    this.$options = options || {}
    let data = (this.$data = options.data)
    let vm = this

    //代理 data上属性代理到vm实例上
    _proxy(data, vm)

    //observe data
    //监测数据变化
    _observe(data, vm)

    //订阅 message
    new Watcher(vm, 'message')
}

function _proxy(data, target) {
    let vm = target
    Object.keys(data).forEach(key => {
        Object.defineProperty(vm, key, {
            enumerable: true,
            configurable: false,
            get: function() {
                return vm.$data[key]
            },
            set: function(newVal) {
                vm.$data[key] = newVal
            }
        })
    })
}

// -----------------observe ------------------------

function _observe(data, vm) {
    if (!data || typeof data !== 'object') {
        return
    }
    return new Observer(data)
}

function Observer(data) {
    this.data = data
    this.walk(data)
}

Observer.prototype.walk = function(data) {
    const keys = Object.keys(data)
    for (let i = 0; i < keys.length; i++) {
        let key = keys[i]
        this.defineReactive(data, key, data[key])
    }
}

Observer.prototype.defineReactive = function(data, key, val) {
    //收集订阅者
    let dep = new Dep()

    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: false,
        get: function() {
            // 由于需要在闭包内添加watcher,所以通过Dep定义一个全局target属性,暂存watcher, 添加完移除
            if (Dep.target) {
                //通过depend方法转到watcher中进行添加,防止重复
                dep.depend()
            }
            return val
        },
        set: function(newVal) {
            val = newVal
            dep.notify()
        }
    })
}

// -----------------observe ------------------------

// ----------------- Dep ------------------------
let uid = 0
function Dep() {
    this.subs = []
    this.uid = uid++
}

Dep.prototype.addSub = function(watcher) {
    this.subs.push(watcher)
}

Dep.prototype.notify = function() {
    this.subs.forEach(watcher => {
        watcher.update()
    })
}

Dep.prototype.depend = function() {
    Dep.target.addDep(this)
}

Dep.target = null

// ----------------- Dep ------------------------

// ----------------- Watcher ------------------------
function Watcher(vm, datakey) {
    this.vm = vm
    this.depIds = {}
    this.datakey = datakey

    //触发 监听数据的get方法, 触发Dep 将自身收集
    this.vaule = this.get()
}

Watcher.prototype.get = function() {
    let datakey = this.datakey
    Dep.target = this
    let value = this.vm.$data[datakey]
    Dep.target = null
    return value
}

Watcher.prototype.addDep = function(dep) {
  //防止重复添加
    if (!this.depIds.hasOwnProperty(dep.uid)) {
        dep.addSub(this)
        this.depIds[dep.uid] = dep
    }
}

Watcher.prototype.update = function() {
    let datakey = this.datakey
    let value = this.get()
    let oldVal = this.value
    if (value !== oldVal) {
        this.value = value
    }
    console.log(`this is ${datakey} : ${this.value}`)
}

// ----------------- Watcher ------------------------
image.png

很关键的一部分就是Watcher.prototype.get里触发 监听数据的get方法。
这样就简单实现了vue的数据绑定,双向绑定无非就是在view层添加了事件监听并触发数据的set,来同步数据。

相关文章

  • 深入Vue响应式原理

    1.Vue的双向数据绑定 参考 vue的双向绑定原理及实现Vue双向绑定的实现原理Object.definepro...

  • vue 双向数据绑定

    Vue实现数据双向绑定的原理:Object.defineProperty()vue实现数据双向绑定主要是:采用数据...

  • 【转】JavaScript的观察者模式(Vue双向绑定原理)

    关于Vue实现数据双向绑定的原理,请点击:Vue实现数据双向绑定的原理原文链接:JavaScript设计模式之观察...

  • 前端理论面试--VUE

    vue双向绑定的原理(详细链接) VUE实现双向数据绑定的原理就是利用了 Object.definePropert...

  • Vue实现数据双向绑定的原理

    Vue实现数据双向绑定的原理:Object.defineProperty() vue实现数据双向绑定主要是:采用数...

  • vue面试知识点

    vue 数据双向绑定原理 vue实现数据双向绑定原理主要是:采用数据劫持结合发布订阅设计模式的方式,通过对data...

  • 2020-09-09

    scoped 混入 数据结构 vue事务 map foreach vue数据双向绑定原理 computed 缓存

  • Vue双向数据绑定原理

    剖析Vue实现原理 - 如何实现双向绑定mvvm 本文能帮你做什么?1、了解vue的双向数据绑定原理以及核心代码模...

  • 关于双向绑定的问题

    剖析Vue实现原理 - 如何实现双向绑定mvvm 本文能帮你做什么?1、了解vue的双向数据绑定原理以及核心代码模...

  • 前端面试题:VUE

    1. vue的双向数据绑定实现原理? 2. vue如何在组件之间进行传值? 3. vuex和vue的双向数据绑定...

网友评论

      本文标题:Vue数据绑定原理

      本文链接:https://www.haomeiwen.com/subject/glbtmctx.html