vue源码学习——vue双向绑定原理

原理

vue充分利用了Object.defineProperty()方法,通过发布订阅模式,在对data属性的读写操作,转换成get/set方法,当数据变化时通知视图更新。

Object.defineProperty()介绍

定义:Object.defineProperty()是直接在对象上定义一个新属性或者可以直接修改一个现有属性,并返回该对象。

let demo={}
Object.defineProperty(demo,"test",{
    get(){
       console.log("test属性被读取")
       return "newTest"
    },
    set(newValue){
      console.log("test属性被重置为"+newValue)
    }
})
demo.test //test属性被读取 "newTest"
demo.test="test123" //test属性被重置为test123  "test123"

通过上面例子可以发现,demo对象访问test属性时候,会先访问Object.defineProperty()中get方法,修改时会访问set方法。

这样我们就可以观察到对象中属性读写的变化。
如果我们想对demo中的所有属性进行监测,需要对属性进行遍历,然后观察:

//遍历对象属性,观察值变化
function observable(obj){
    if(!obj||typeof obj!=="object") return
    for(let key in obj){
        defineReactive (obj,key)
    }
}

//单个属性监测
function defineReactive (obj,key){
    Object.defineProperty(obj,key,{
        get(){
            console.log(`${key}属性被读取了`);
            return obj[key];
        },
        set(newVal){
            console.log(`${key}属性被修改了`);
            val = newVal;
        }
    })
}

发布订阅模式

在数据被读或写的时候通知那些依赖该数据的视图更新了,为了方便,我们需要先将所有依赖收集起来,一旦数据发生变化,就统一通知更新。其实,这就是典型的“发布订阅者”模式,数据变化为“发布者”,依赖对象为“订阅者”。
创建消息订阅:

class Dep {
    constructor(){
        this.subs = []  //搜集依赖,即哪些视图需要更新
    },
    //增加订阅者
    addSub(sub){
        this.subs.push(sub);
    },
    //判断是否增加订阅者
    depend () {
        if (Dep.target) {
             this.addSub(Dep.target)
        }
    },

    //通知订阅者更新
    notify(){
        this.subs.forEach((sub) =>{
            sub.update()
        })
    }
}
Dep.target = null;

将订阅方法,增加至defineReative方法中:

function defineReactive (obj,key,val) {
		let dep = new Dep();
		Object.defineProperty(obj, key, {
			get(){
				dep.depend();  //读取属性时,增加订阅者
				console.log(`${key}属性被读取了`);
				return val;
			},
			set(newVal){
				val = newVal;
				console.log(`${key}属性被修改了`);
				dep.notify()                    //数据变化通知所有订阅者
			}
		})
	}

订阅者Watcher

class Watcher {
		constructor(vm,exp,cb){
		    this.vm = vm;  //vue实例
		    this.exp = exp; //是node节点的v-model或v-on:click等指令的属性值
		    this.cb = cb; //Watcher绑定的更新函数;
		    this.value = this.get();  // 初始化时,将自己添加到订阅器的操作
		},

		update(){
			let value = this.vm.data[this.exp];
        	let oldVal = this.value;
        	if (value !== oldVal) {
                this.value = value;
                this.cb.call(this.vm, value, oldVal);
			},
		get(){
			Dep.target = this;  // 缓存自己
        	let value = this.vm.data[this.exp]  // 强制执行监听器里的get函数
        	Dep.target = null;  // 释放自己
        	return value;
		}
	}

vue实现双向绑定

在这里插入图片描述

  • observer用来实现对每个vue中的data中定义的属性循环用Object.defineProperty()实现数据劫持,以便利用其中的setter和getter,然后通知订阅者,订阅者会触发它的update方法,对视图进行更新。
  • 在vue中v-model,v-name,{{}}等都可以对数据进行显示,也就是说假如一个属性都通过这三个指令了,那么每当这个属性改变的时候,相应的这个三个指令的html视图也必须改变,于是vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者,其订阅者只是更新自己的指令对应的数据,也就是v-model='name’和{{name}}有两个对应的订阅者,各自管理自己的地方。每当属性的set方法触发,就循环更新Dep中的订阅者。

参考链接:https://www.cnblogs.com/wangjiachen666/p/9883916.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值