- 首先,当创建Vue实例时,传入的
data
选项会被转换为响应式对象。每个属性都被转换为"getter/setter",并被添加到Vue的响应式系统中。
var vm = new Vue({
data: {
message: 'Hello Vue!'
}
});
- 在模板中,将
message
属性与输入框进行双向绑定,使用v-model
指令。
<input type="text" v-model="message">
<p>{{ message }}</p>
这里,v-model="message"
告诉Vue将输入框的值与message
属性进行双向绑定。
-
当用户在输入框中输入内容时,触发了输入事件。Vue会捕获这个事件,并自动更新
message
属性。 -
通过实现"getter/setter",Vue能够在
message
属性被读取或修改时进行依赖追踪。
// 转化后的data对象
{
message: 'Hello Vue!',
// 生成的getter/setter
get message() {
// 依赖追踪:如果有相关的视图依赖该属性,则将其添加到依赖列表中
/* ... */
return this._data.message;
},
set message(value) {
// 响应变化:在属性被修改时,触发视图更新
/* ... */
this._data.message = value;
}
}
- 当
message
属性被更新时,Vue的响应式系统会通知相关的视图进行更新。
在上述代码中,当用户在输入框中输入内容时,message
属性会自动更新,并且对应的{{ message }}
表达式也会实时更新显示最新的值。
这就是Vue.js的数据双向绑定原理:通过响应式系统追踪数据变化,并利用指令建立数据与视图之间的关联,从而实现数据和视图的双向同步。
Vue的双向数据绑定是通过数据劫持和发布-订阅模式来实现的。
具体来说,当Vue实例创建时,它会遍历所有的属性,并使用Object.defineProperty()方法将这些属性转换成getter/setter。这样,当数据发生变化时,Vue能够检测到变化并通知所有相关的组件。
在一个双向绑定的场景中,当用户修改视图中的数据时,这些修改会触发Vue的setter方法,setter方法会通知所有相关的组件,然后更新视图。反过来,如果数据在Vue实例中发生了变化,getter方法会被调用,这会触发更新视图的操作。
这种数据劫持的方式可以实现响应式的数据绑定,因为Vue能够监测数据的变化并立即更新相关的组件。同时,Vue还使用发布-订阅模式来管理组件之间的通信,确保所有组件都能接收到更新的通知并及时响应变化。
Vue 在创建实例时会调用 Observer 方法来遍历 data 中的数据并进行数据劫持。以下是一个简单的示例代码:
function Observer(data) {
this.data = data;
this.walk(data);
}
Observer.prototype = {
walk: function(data) {
var self = this;
Object.keys(data).forEach(function(key) {
self.defineReactive(data, key, data[key]);
});
},
defineReactive: function(data, key, val) {
var dep = new Dep();
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
if (Dep.target) {
dep.addSub(Dep.target);
}
return val;
},
set: function(newVal) {
if (val === newVal) {
return;
}
val = newVal;
dep.notify();
}
});
}
};
function observe(data) {
if (!data || typeof data !== 'object') {
return;
}
return new Observer(data);
}
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
};
Dep.target = null;
上面的代码中,Observer 方法遍历 data 中的所有属性并为每个属性添加了 getter 和 setter 方法,这样当这个属性被读取或被赋值时,就能触发 getter 或 setter 方法中的相关逻辑。这里使用了 Object.defineProperty 方法来实现数据劫持。
在 defineReactive 方法中,我们使用了一个数组 subs 来保存所有订阅该属性的 watcher 对象,当属性值变化时,set 方法会遍历 subs 数组并调用每个 watcher 对象的 update 方法,从而更新视图。
同时,我们还定义了一个 Dep 类来管理 watcher 对象,并实现了一个全局变量 Dep.target 用来临时存储当前的 watcher 对象。在 get 方法中,我们判断 Dep.target 是否存在,如果存在则将当前的 watcher 对象添加到 subs 数组中,从而实现了订阅者模式。