单向绑定是将模型(Model)的数据绑定到视图(View)上,当模型数据发生变化时,视图会自动更新。而双向绑定在此基础上增加了视图到模型的反馈机制,即视图状态的变化也能自动反映到模型数据上。
以表单输入为例,当用户在输入框中填写信息时,视图的状态发生变化,如果这种变化能实时更新到模型数据,那么就实现了双向绑定。
双向绑定是MVVM(Model-View-ViewModel)架构的核心特性之一。在MVVM中,ViewModel负责将模型和视图关联起来,实现数据的双向流动。
ViewModel包含两个主要部分:
在Vue中,双向绑定的流程包括以下几个步骤:
以下是对Vue双向绑定关键部分的代码实现:
// Vue构造函数
class Vue {
constructor(options) {
this.$options = options;
this.$data = options.data;
observe(this.$data);
proxy(this);
new Compile(options.el, this);
}
}
// 数据响应化处理
function observe(obj) {
if (typeof obj !== "object" || obj == null) {
return;
}
new Observer(obj);
}
class Observer {
constructor(value) {
this.value = value;
this.walk(value);
}
walk(obj) {
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key]);
});
}
}
// 编译器
class Compile {
constructor(el, vm) {
this.$vm = vm;
this.$el = document.querySelector(el);
if (this.$el) {
this.compile(this.$el);
}
}
compile(el) {
const childNodes = el.childNodes;
Array.from(childNodes).forEach((node) => {
if (this.isElement(node)) {
console.log("编译元素" + node.nodeName);
} else if (this.isInterpolation(node)) {
console.log("编译插值文本" + node.textContent);
}
if (node.childNodes && node.childNodes.length > 0) {
this.compile(node);
}
});
}
isElement(node) {
return node.nodeType == 1;
}
isInterpolation(node) {
return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
}
}
// 依赖收集
class Watcher {
constructor(vm, key, updater) {
this.vm = vm;
this.key = key;
this.updaterFn = updater;
Dep.target = this;
vm[key];
Dep.target = null;
}
update() {
this.updaterFn.call(this.vm, this.vm[this.key]);
}
}
class Dep {
constructor() {
this.deps = [];
}
addDep(dep) {
this.deps.push(dep);
}
notify() {
this.deps.forEach((dep) => dep.update());
}
}
function defineReactive(obj, key, val) {
this.observe(val);
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
Dep.target && dep.addDep(Dep.target);
return val;
},
set(newVal) {
if (newVal === val) return;
dep.notify();
},
});
}