vue.js观察者模式+双向绑定

首先说下什么是mvvm?或者你是怎样理解mvvm的?

首先我说下MVC: Model-View-Controller,字面意思其实就是3种东西,以前搞过前后不分离的时候特别有感受,m是数据,(控制器c)里面获取到了(数据m )后,写入需要渲染的(视图view)模板,然后视图就显示了。

接着到mvvm 其实还是3种东西,前面2个不变,数据、视图,变得最后一个就是vm(视图数据

来个形象点的比喻:vue的单文件是以template、script  style 三层分开的。

<script >就可以表示为m数据   <template>表示为vm,  界面肯定就是v了

因此:在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上

不需要手动操作DOM, 不需要关注数据状态的同步问题

m<->vm<->v 

什么是双向绑定?

道理你们都懂,我就不再打字了。无非就是数据在view,model同步改变了。

代码非常简单,非常容易理解。vue的双向绑定也是这个原理。

不过人家那个是使用了 发布-订阅模式结合defineProperty进行循环遍历对象key属性值进行监听,下面这个demo只是把对象key值写死而已。

 在new Vue的时候 先进行数据转换+执行new Wacher监听

Vue响应系统,其核心有三点:observe、watcher、dep:
observe:遍历data中的属性,使用 Object.defineProperty 的get/set方法对其进行数据劫持,
         顺便在get加入了dep依赖收集,在set里面加入了触发dep依赖更新

dep:每个属性拥有自己的消息订阅器dep,用于存放所有订阅了该属性的观察者对象

watcher:观察者(对象),通过dep实现对响应属性的监听,监听到结果后,
主动触发自己的回调进行响应 (执行observe的时候会初始化这个观察者)


这里描述下new Vue过程:

当new Vue的时候会先执行:observe ,
使用 Object.defineProperty 的get/set方法对其进行数据劫持,
顺便在get加入了dep依赖收集其实就是新增订阅者(watcher的观察者),在set里面加入了触发dep的依赖更新(就是观察者的更新函数),
最后顺便会 Dep.target = null; 清空观察者

第二步:然后new watcher 
            Dep.target = this;  // 缓存自己  上面的依赖收集的就是这个
        	let value = this.vm.data[this.exp]  // 强制执行监听器里的get函数
        	Dep.target = null;  // 释放自己
      这里会又执行一遍上面的get函数

<script src="observer.js"></script>
<script src="watcher.js"></script>
<script>
	function myVue (data, el, exp) {
	    this.data = data;
	    observable(data);                      //将数据变的可观测
	    el.innerHTML = this.data[exp];           // 初始化模板数据的值

        //在new Vue的时候 先进行数据转换+执行new Wacher监听 
	    new Watcher(this, exp, function (value) {
	        el.innerHTML = value;
	    });
	    return this;
	}

	var ele = document.querySelector('#name');
	var input = document.querySelector('input');
	
    var myVue = new myVue({
		name: 'hello world'
	}, ele, 'name');
 	
	//改变输入框内容
    input.oninput = function (e) {
    	myVue.data.name = e.target.value
    }
	//改变data内容
	function changeInput(){
		myVue.data.name = "难凉热血"
	
	}
</script>

 observer.js  (把data属性遍历转化成可监测的,里面包含了Dep依赖收集器)

/**
	 * 把一个对象的每一项都转化成可观测对象
	 * @param { Object } obj 对象
	 */
	function observable (obj) {
		if (!obj || typeof obj !== 'object') {
        	return;
    	}
		let keys = Object.keys(obj);
		keys.forEach((key) =>{
			defineReactive(obj,key,obj[key])
		})
		return obj;
	}
	/**
	 * 使一个对象转化成可观测对象
	 * @param { Object } obj 对象
	 * @param { String } key 对象的key
	 * @param { Any } val 对象的某个key的值
	 */
	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()                    //数据变化通知所有订阅者
			}
		})
	}
	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;

 watcher.js 

class Watcher {
		constructor(vm,exp,cb){
		    this.vm = vm;
		    this.exp = exp;
		    this.cb = cb;
		    this.value = this.get();  // 将自己添加到订阅器的操作
		}
		get(){
			Dep.target = this;  // 缓存自己
        	let value = this.vm.data[this.exp]  // 强制执行监听器里的get函数
        	Dep.target = null;  // 释放自己
        	return value;
		}
		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);
			}
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值