1. 这里有一个简单的响应式原理 

2. 如果说需要我手写出来那还是有一定难度,在这里总结一下:

  首先,Vue 利用 Object.defineProperty 实现了数据劫持;

  属性分为两种,一种是数据属性,另一种是访问器属性。在访问器属性下,可以利用 Object.defineproperty 描述符对象定义四个特性,分别是:

(1)configurable:表示一个属性的特性能否被修改,能否被 delete 删除并重新定义

(2)enumerable:表示一个属性能否被 for...in... 循环返回

(3)get:它是一个 getter 方法,在这里可以进行数据劫持,当我们访问一个属性时,就会进入此方法

(4)set:它是一个 setter 方法,在这里可以进行数据劫持,当我们修改一个属性值,就会进入此方法

接下来,来看一个简单的数据劫持示例:

页面显示部分:



<body>
<h2></h2>
</body>


JS代码:



<script>
function Observer(obj) {
for (let key in obj) {
defineReactive(obj, key);
}
}

function defineReactive(obj, key) {
let val = obj[key];
Object.defineProperty(obj, key, {
get() {
console.log("getter executed.");
return val;
},
set(newValue) {
if (val === newValue) {
return;
}
val = newValue;
console.log("setter executed");
}
})
}

function Vue(options) {
const self = this;
if (options && typeof options.data === 'function') {
this._data = options.data.apply(this);
}
Observer(this._data);
}

const datas = {
message: "Hello myReactive."
};

const vue = new Vue({
data() {
return datas;
}
});

let h2 = document.querySelector('h2');
h2.innerHTML = datas.message;
</script>


Vue 响应式原理_数据绑定

   如果改变了 datas 中 message 的值,那么结果如下:

Vue 响应式原理_方法调用_02

   可以看到 get 和 set 方法都顺利执行,我们在获取数据和改变数据时,都能在这两个方法内部定义我们自己想要的操作。

  其次,Vue 利用了 发布-订阅 模式,实现了对数据的视图更新。

  我们知道,当我们在 Vue 中可能不止一处调用一个 data 中的属性,比如:1. 使用 Mustache 语法在页面上显示属性值 2. 在 computed 中使用属性值 等等...... 那么我们的数据发生变化时,所有这些用到属性的地方,都需要去更新数据,那么它可以利用 Watcher 来对一处数据进行控制。一个属性可能会有多个 Watcher,当数据更新时,需要有一个 Dep 作为发布者,通知所有 Watcher 去进行 update 操作,意味着一个属性会对应一个 Dep.

  也就是说,Object.defineProperty 实现了数据劫持,Dep 【依赖收集器】负责依赖管理,实现了一个发布订阅模式【在 get 方法中收集 Watcher】,当数据有更新时 notify 通知所有 Watcher【在 set 方法调用】,Watcher 是实际上的数据依赖,当数据变化时通过 Watcher 的 update 实现视图更新。

 3. v-model 双向数据绑定:上面所讲的 Vue 响应式原理是单项数据绑定,即属性变化,视图更新,那我们常见的 input 输入使用 v-model,导致属性值变化,是怎么一回事?

  原理就是给 input 添加 oninput 原生 JavaScript 事件,一旦有输入改变属性值,则触发属性的 set 方法,将输入值更新给 data 属性。