vue的数据双向绑定
Posted xjy20170907
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue的数据双向绑定相关的知识,希望对你有一定的参考价值。
vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者;
2.实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图;
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在v-model,v-on等指令,则解析器Compile初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher);
a、Observer实现对MVVM自身model数据劫持,监听数据的属性变更,并在变动时进行notify;
b、Compile实现指令解析,初始化视图,并订阅数据变化,绑定好更新函数;
c、Watcher一方面接收Observer通过dep传递过来的数据变化,一方面通知Compile进行view update;
b、Compile实现指令解析,初始化视图,并订阅数据变化,绑定好更新函数;
c、Watcher一方面接收Observer通过dep传递过来的数据变化,一方面通知Compile进行view update;
先初始化一个监听器Observer,用于监听该对象data属性的值。
然后初始化一个解析器Compile,绑定这个节点,并解析其中的v-," {{}} "指令,(每一个指令对应一个Watcher)并初始化模板数据以及初始化相应的订阅者,并把订阅者添加到订阅器中(Dep)。这样就实现双向绑定了。
然后初始化一个解析器Compile,绑定这个节点,并解析其中的v-," {{}} "指令,(每一个指令对应一个Watcher)并初始化模板数据以及初始化相应的订阅者,并把订阅者添加到订阅器中(Dep)。这样就实现双向绑定了。
整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁
data如何更新view
使用observer(观察者)实现数据劫持,绑定watcher(订阅者)的更新函数,watcher通知compile(模板编译)更新虚拟dom,最终挂载真实dom上
view如何更新data
使用事件监听函数绑定事件,在事件中改变值触发observer的set,再触发watcher实现observer
function defineReactive(data, key, val) { observe(val); // 递归遍历所有子属性 var dep = new Dep(); Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function() { if (是否需要添加订阅者) { dep.addSub(watcher); // 在这里添加一个订阅者 } return val; }, set: function(newVal) { if (val === newVal) { return; } val = newVal; console.log(‘属性‘ + key + ‘已经被监听了,现在值为:“‘ + newVal.toString() + ‘”‘); dep.notify(); // 如果数据变化,通知所有订阅者 } }); } function Dep () { this.subs = []; } Dep.prototype = { addSub: function(sub) { this.subs.push(sub); }, notify: function() { this.subs.forEach(function(sub) { sub.update(); }); } };
实现watcher
function Watcher(vm, exp, cb) { // vm为当前dom实例,exp为指令绑定的动态数据或{{}}里的动态数据,cb为更新函数 this.cb = cb; this.vm = vm; this.exp = exp; this.value = this.get(); // 将自己添加到订阅器的操作 } Watcher.prototype = { update: function() { this.run(); }, run: function() { var value = this.vm.data[this.exp]; var oldVal = this.value; if (value !== oldVal) { this.value = value; this.cb.call(this.vm, value, oldVal); } }, get: function() { Dep.target = this; // 缓存自己 var value = this.vm.data[this.exp] // 强制执行监听器里的get函数 Dep.target = null; // 释放自己 return value; } };
实现compile
作用:
1 解析模板指令,并替换模板数据,初始化视图;
2 将模板指令对应的节点绑定对应的更新函数,初始化相应的订阅器;
首先需要解析的dom节点存入fragment片段里再进行处理:
function compileElement (el) { var childNodes = el.childNodes; var self = this; [].slice.call(childNodes).forEach(function(node) { var reg = /{{(.*)}}/; var text = node.textContent; if (self.isTextNode(node) && reg.test(text)) { // 判断是否是符合这种形式{{}}的指令 self.compileText(node, reg.exec(text)[1]); } if (node.childNodes && node.childNodes.length) { self.compileElement(node); // 继续递归遍历子节点 } }); }, function compileText (node, exp) { var self = this; var initText = this.vm[exp]; this.updateText(node, initText); // 将初始化的数据初始化到视图中 new Watcher(this.vm, exp, function (value) { // 生成订阅器并绑定更新函数 self.updateText(node, value); }); }, function (node, value) { node.textContent = typeof value == ‘undefined‘ ? ‘‘ : value; }
原文: https://www.jianshu.com/p/f194619f6f26
以上是关于vue的数据双向绑定的主要内容,如果未能解决你的问题,请参考以下文章