什么是双向数据绑定?
Vue 是一个 MVVM 框架,数据绑定简单来说,就是当数据发生变化时,相应的视图会进行更新,当视图更新时,数据也会跟着变化。
Vue.js 则是通过数据劫持以及结合发布者-订阅者来实现的,数据劫持是利用 ES5 的 Object.defineProperty(obj, key, val)来劫持各个属性的的 setter 以及 getter,在数据变动时发布消息给订阅者,从而触发相应的回调来更新视图。
1.实现最基础的数据绑定
<div id="app">
<input type="text" v-model="inp">
输入的值为:{{inp}}
<div>
<input type="text" v-model="inp">
</div>
</div>
<script>
var vm = new MVue({
el: '#app',
data: {
inp: 'hello world'
}
})
</script>
实现思路:
1、输入框以及文本节点和 data 中的数据进行绑定
2、输入框内容变化时,data 中的对应数据同步变化,即 view => model
3、data 中数据变化时,对应的文本节点内容同步变化 即 model => view
双向数据绑定原理图
2、实现 Compile
compile 主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图,如图所示:
function compile(node, vm) {
let reg = /\{\{(.*)\}\}/;
// 元素节点
if (node.nodeType === 1) {
var attrs = node.attributes;
for (let attr of attrs) {
if (attr.nodeName === 'v-model') {
// 获取v-model指令绑定的data属性
var name = attr.nodeValue;
// 绑定事件
node.addEventListener('input', function(e) {
vm.$data[name] = e.target.value;
});
// 初始化数据绑定
node.value = vm.$data[name];
// 移除v-model 属性
node.removeAttribute('v-model');
}
}
}
// 文本节点
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
var name = RegExp.$1 && RegExp.$1.trim();
// 绑定数据到文本节点中
node.nodeValue = node.nodeValue.replace(
new RegExp('\\{\\{\\s*(' + name + ')\\s*\\}\\}'),
vm.$data[name]
);
}
}
}
修改下 MVue 构造函数,增加模板编译:
function MVue(options) {
this.$el = options.el;
this.$data = options.data;
// 模板编译
let elem = document.querySelector(this.$el);
elem.appendChild(nodeToFragment(elem, this));
}
3.实现 watcher
function Watcher(vm, node, name, type) {
Dep.target = this;
this.name = name;
this.node = node;
this.vm = vm;
this.type = type;
this.update();
Dep.target = null;
}
Watcher.prototype = {
update: function() {
this.get();
this.node[this.type] = this.value; // 订阅者执行相应操作
},
// 获取data的属性值
get: function() {
console.log(1);
this.value = this.vm[this.name]; //触发相应属性的get
}
};
4.实现 Dep 来为每个属性添加订阅者
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。