Vue之九数据劫持实现MVVM的数据双向绑定

vue是通过数据劫持的方式来做数据绑定的,其中最核心的方法便是通过Object.defineProperty()来实现对属性的劫持,达到监听数据变动的目的。

如果不熟悉defineProperty,猛戳这里

整理了一下,要实现mvvm的双向绑定,就必须要实现以下几点:
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
4、mvvm入口函数,整合以上三者

1、实现Observer

我们知道可以利用Obeject.defineProperty()来监听属性变动
那么将需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 settergetter
这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。。相关代码可以是这样:

var data = {name: ‘kindeng‘};
observe(data);
data.name = ‘dmq‘; // 监听到值变化了 kindeng --> dmq

function observe(data) {
    if (!data || typeof data !== ‘object‘) {
        return;
    }
    // 取出所有属性遍历
    Object.keys(data).forEach(function(key) {
        defineReactive(data, key, data[key]);
    });
};

function defineReactive(data, key, val) {
    observe(val); // 监听子属性
    Object.defineProperty(data, key, {
        enumerable: true, // 可枚举
        configurable: false, // 不能再define
        get: function() {
            return val;
        },
        set: function(newValue) {
            console.log(‘哈哈哈,监听到值变化了 ‘, val, ‘ --> ‘, newValue);
            val = newValue;
        }
    });
}

  上面的思路整理中我们已经明确订阅者应该是Watcher, 而且var dep = new Dep();是在 defineReactive方法内部定义的,所以想通过dep添加订阅者

完整方法	function Observer(data) {
			this.data = data;
			this.walk(data);
		}

		Observer.prototype = {
			walk: function(data) {
				var me = this;
				Object.keys(data).forEach(function(key) {
					me.convert(key, data[key]);
				});
			},
			convert: function(key, val) {
				this.defineReactive(this.data, key, val);
			},

			defineReactive: function(data, key, val) {
				var dep = new Dep();
				var childObj = observe(val);
				Object.defineProperty(data, key, {
					enumerable: true, // 可枚举
					configurable: false, // 不能再define
					get: function() {
						if(Dep.target) {
							dep.depend();
						}
						return val;
					},
					set: function(newVal) {
						if(newVal === val) {
							return;
						}
						val = newVal;
						// 新的值是object的话,进行监听
						childObj = observe(newVal);
						// 通知订阅者
						dep.notify();
					}
				});
			}
		};

		function observe(value, vm) {
			if(!value || typeof value !== ‘object‘) {
				return;
			}

			return new Observer(value);
		};

		var uid = 0;

		function Dep() {
			this.id = uid++;
			this.subs = [];
		}

		Dep.prototype = {
			addSub: function(sub) {
				this.subs.push(sub);
				console.log(this.subs)
			},

			depend: function() {
				Dep.target.addDep(this);
			},

			removeSub: function(sub) {
				var index = this.subs.indexOf(sub);
				if(index != -1) {
					this.subs.splice(index, 1);
				}
			},

			notify: function() {
				this.subs.forEach(function(sub) {
					sub.update();
				});
			}
		};

		Dep.target = null;

  这里已经实现了一个Observer了,已经具备了监听数据和数据变化通知订阅者的功能,那么接下来就是实现Compile了

2、实现Compile

compile主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图,如图所示:

因为遍历解析的过程有多次操作dom节点,为提高性能和效率,会先将跟节点el转换成文档碎片fragment进行解析编译操作,解析完成,再将fragment添加回原来的真实dom节点中

3、实现Watcher

Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。

实例化Watcher的时候,调用get()方法,通过Dep.target = watcherInstance标记订阅者是当前watcher实例,强行触发属性定义的getter方法,getter方法执行的时候,就会在属性的订阅器dep添加当前watcher实例,从而在属性值有变化的时候,watcherInstance就能收到更新通

4、实现MVVM

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

知。

function MVVM(options) {
    this.$options = options;
    var data = this._data = this.$options.data, me = this;
    // 属性代理,实现 vm.xxx -> vm._data.xxx
    Object.keys(data).forEach(function(key) {
        me._proxy(key);
    });
    observe(data, this);
    this.$compile = new Compile(options.el || document.body, this)
}

MVVM.prototype = {
    _proxy: function(key) {
        var me = this;
        Object.defineProperty(me, key, {
            configurable: false,
            enumerable: true,
            get: function proxyGetter() {
                return me._data[key];
            },
            set: function proxySetter(newVal) {
                me._data[key] = newVal;
            }
        });
    }
};

  这里主要还是利用了Object.defineProperty()这个方法来劫持了vm实例对象的属性的读写权,使读写vm实例的属性转成读写了vm._data的属性值

原文地址:https://www.cnblogs.com/yiyi17/p/8445259.html

时间: 2024-08-29 22:08:48

Vue之九数据劫持实现MVVM的数据双向绑定的相关文章

Vue.js双向绑定的实现原理

Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统.本文仅探究几乎所有Vue的开篇介绍都会提到的hello world双向绑定是怎样实现的.先讲涉及的知识点,再参考源码,用尽可能少的代码实现那个hello world开篇示例. 参考文章:https://segmentfault.com/a/1190000006599500 一.访问器属性 访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过defineProperty()方法单独定义. var obj = {

vue之v-model表单输入双向绑定

vue的核心:声明式的指令和数据的双向绑定. 那么声明式的指令,已经给大家介绍完了.接下来我们来研究一下什么是数据的双向绑定? 另外,大家一定要知道vue的设计模式:MVVM M是Model的简写,V是View的简写,VM就是ViewModel. 单向绑定和双向绑定的区别: 单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新. 有单向绑定,就有双向绑定. 如果用户更新了View,Model的数据也自动被更新了,这种情况就是双向

mvvm双向绑定机制的原理和代码实现

mvvm框架的双向绑定,即当对象改变时,自动改变相关的dom元素的值,反之,当dom元素改变时,能自动更新对象的值,当然dom元素一般是指可输出的input元素. 1. 首先实现单向绑定,在指定对象的属性值发生改变时触发callback函数. 2. 单向绑定可采用ES5新增的defineProperty实现(或defineProperties),用了ES5注定就不支持IE9以下了,为了防止递归死循环问题,原有属性需要剪切到一个私有属性中保存. 3. 循环调用defineProperty定义闭包时

Vue.js-----轻量高效的MVVM框架(九、组件利用Props传递数据)

#使用props传递数据 html:传递普通的字符串 <h3>#使用props传递数据</h3> <div id="dr01"> <div>组件实例的作用域是孤立的.这意味着不能并且不应该在子组件的模板内直接引用父组件的数据.可以使用 props 把数据传给子组件.</div> <br /> <child msg="hello, vue.js!"></child> <

Vue框架核心之数据劫持

本文来自网易云社区. 前瞻 当前前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Angular.Regular.Vue.React等等,它们最大的优点就是可以实现数据绑定,再也不需要手动进行DOM操作了,它们实现的原理也基本上是脏检查或数据劫持.那么本文就以Vue框架出发,探索作者运用Object.defineProperty来实现数据劫持的奥秘(本文所选取的相关代码源自于Vue v2.0.3版本的源码). 回顾一下Object.defineProperty 语法  Obje

vue双向绑定(数据劫持+发布者-订阅者模式)

参考文献:https://www.cnblogs.com/libin-1/p/6893712.html 实现mvvm主要包含两个方面,数据变化更新视图,视图变化更新数据. 关键点在于data如何更新view,因为view更新data其实可以通过事件监听即可.我们着重来分析,当数据改变,如何更新视图的. 如何知道数据变了,就是通过Object.defineProperty( )对属性设置一个set函数,当数据改变了就会来触发这个函数,所以我们只要将一些需要更新的方法放在这里面,就可以实现data更

[Vue源码]一起来学Vue双向绑定原理-数据劫持和发布订阅

有一段时间没有更新技术博文了,因为这段时间埋下头来看Vue源码了.本文我们一起通过学习双向绑定原理来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vue模板编译原理(一)-Template生成AST 一起来学Vue模板编译原理(二)-AST生成Render字符串 一起来学Vue虚拟DOM解析-Virtual Dom实现和Dom-diff算法 这些文章统一放在我的git仓库:https://github.com/yzsun

剖析Vue原理&amp;实现双向绑定MVVM

剖析Vue原理&实现双向绑定MVVM vue.js 双向绑定 javascript 邓木琴居然被盗用了 2016年08月16日发布 推荐 24 推荐 收藏 195 收藏,10.6k 浏览 本文能帮你做什么?1.了解vue的双向数据绑定原理以及核心代码模块2.缓解好奇心的同时了解如何实现双向绑定为了便于说明原理与实现,本文相关代码主要摘自vue源码, 并进行了简化改造,相对较简陋,并未考虑到数组的处理.数据的循环依赖等,也难免存在一些问题,欢迎大家指正.不过这些并不会影响大家的阅读和理解,相信看完

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

Vue实现数据双向绑定的原理:Object.defineProperty() vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调.当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter.用户看不