Vue的响应原理

渲染render function之后就是 核心的响应式过程了
Object.defineProperty
vue的核心之一就是Object.defineProperty 方法(IE9及其以上)
Object.defineProperty(obj, prop, descriptor) //可以定义新的属性或者修改原来的属性
参数说明
obj:必需,目标对象; prop:必需,需要定义或者修改的属性名;descriptor:添加对要操作属性的描述
var obj = {};
Object.defineProperty(obj, 'msg', {
get () {
console.log('get');
},
set (newVal) {
console.log('set', newVal);
}
});
obj.msg // get
obj.msg = 'hello world' // set hello world
取 obj 对象中 msg 的值时会调用 get 方法,给 msg 赋值时会调用 set 方法,并接收新值作为其参数。
这里提一句,在 Vue 中我们调用数据是直接 this.xxx ,而数据其实是 this.data.xxx,原来 Vue 在初始化数据的时候会遍历 data 并代理这些数据。
Object.keys(this.data).forEach((key) => {
this.proxyKeys(key);
});
proxyKeys (key) {
Object.defineProperty(this, key, {
enumerable: false,
configurable: true,
get() {
return this.data[key];
},
set(newVal) {
this.data[key] = newVal;
}
});
}
上面可以看到,取 this.key 的值其实是取 this.data.key 的值,赋值同理。
现在,我们已经知道如何去检测数据的变化,并且做出一些响应了。
观察者模式(发布者-订阅者模式)
vue 的响应式系统依赖于三个重要的类:Dep 类、Watcher 类、Observer 类。
Dep 类作为发布者的角色,Watcher 类作为订阅者的角色,Observer 类则是连接发布者和订阅者的纽带,决定订阅和发布的时机。
我们先看下面的代码,来对发布者和订阅者有个初步的了解。
class Dep {
constructor() {
this.subs = [];
}
addSub(watcher) {
this.subs.push(watcher);
}
notify() {
this.subs.forEach(watcher => {
watcher.update();
});
}
}
class Watcher {
constructor() {
}
update() {
// 接收通知后的处理方法
}
}
const dep = new Dep(); // 发布者 dep
const watcher1 = new Watcher(); // 订阅者1 watcher1
const watcher2 = new Watcher(); // 订阅者2 watcher2
dep.addSub(watcher1); // watcher1 订阅 dep
dep.addSub(watcher2); // watcher2 订阅 dep
dep.notify(); // dep 发送通知
面我们定义了一个发布者 dep,两个订阅者 watcher1、watcher2。让 watcher1、watcher2 都订阅 dep,当 dep 发送通知时,watcher1、watcher2 都能做出各自的响应。
现在我们已经了解了发布者和订阅者的关系,那么剩下的就是订阅和发布的时机。什么时候订阅?什么时候发布?想到上面提到的 Object.defineProperty ,想必你已经有了答案。
我们来看 Observer 类的实现:
class Observer {
constructor(data) {
this.data = data;
this.walk();
}
walk() {
Object.keys(this.data).forEach(key => {
this.defineReactive(this.data, key, this.data[key]);
});
}
defineReactive(data, key, value) {
const dep = new Dep();
if ( value && typeof value === 'object' ) {
new Observer(value);
}
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get() {
if (Dep.target) {
dep.addSub(Dep.target); // 订阅者订阅 Dep.target 即当前 Watcher 类的实例(订阅者)
}
return value;
},
set(newVal) {
if (newVal === value) {
return false;
}
value = newVal;
dep.notify(); // 发布者发送通知
}
});
}
}
在 Observer 类中,为 data 的每个属性都实例化一个 Dep 类,即发布者。并且在取值时让订阅者(有多个,因为 data 中的每个属性都可以被应用在多个地方)订阅,在赋值时发布者发布通知,让订阅者做出各自的响应。
这里需要提的是 Dep.target,这其实是 Watcher 类的实例,我们可以看看 Watcher 的详细代码:
class Watcher {
constructor(vm, exp, cb) {
this.vm = vm;
this.exp = exp; // data 属性名
this.cb = cb; // 回调函数
// 将自己添加到订阅器
this.value = this.getValue();
}
update() {
const value = this.vm.data[this.exp];
const oldValue = this.value;
if (value !== oldValue) {
this.value = value;
this.cb.call(this.vm, value, oldValue); // 执行回调函数
}
}
getValue() {
Dep.target = this; // 将自己赋值给 Dep.target
const value = this.vm.data[this.exp]; // 取值操作触发订阅者订阅
Dep.target = null;
return value;
}
}
Watcher 类在构造函数中执行了一个 getValue 方法,将自己赋值给 Dep.target ,并且执行了取值操作,这样就成功的完成了订阅操作。一旦数据发生变化,即有了赋值操作,发布者就会发送通知,订阅者就会执行自己的 update 方法来响应这次数据变化
数据的双向绑定
数据的双向绑定即数据和视图之间的同步,视图随着数据变化而变化,反之亦然。我们知道 Vue 是支持数据的双向绑定的,主要应用于表单,是通过 v-model 指令来实现的。而通过上面介绍的知识我们是可以知道如何实现视图随着数据变化的,那么如何让数据也随着视图变化而变化呢?其实也很简单,只要给有 v-model 指令的节点监听相应的事件即可,在事件回调中来改变相应的数据。这一切都 Compile 类中完成,假设有一个 input 标签应用了 v-model 指令,在开始编译模板时,遇到 v-model 指令时会执行:更新 dom 节点的值,订阅者订阅,事件监听。
compileModel (node, vm, exp) {
let val = vm[exp];
// 更新内容
this.modelUpdater(node, val);
// 添加订阅
new Watcher(vm, exp, (value) => {
// 数据改变时的回调函数
this.modelUpdater(node, value);
});
// 事件监听
node.addEventListener('input', (e) => {
const newValue = e.target.value;
if (val === newValue) {
return false;
}
vm[exp] = newValue;
val = newValue;
});
}
Vue的响应原理的更多相关文章
- 仿VUE创建响应式数据
VUE对于前端开发人员都非常熟悉了,其工作原理估计也都能说的清个大概,具体代码的实现估计看的人不会太多,这里对vue响应式数据做个简单的实现. 先简单介绍一下VUE数据响应原理,VUE响应数据分为对象 ...
- vue.js响应式原理解析与实现
vue.js响应式原理解析与实现 从很久之前就已经接触过了angularjs了,当时就已经了解到,angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很 ...
- vue深入响应式原理
vue深入响应式原理 深入响应式原理 — Vue.jshttps://cn.vuejs.org/v2/guide/reactivity.html 注意:这里说的响应式不是bootsharp那种前端UI ...
- Vue 数据响应式原理
Vue 数据响应式原理 Vue.js 的核心包括一套“响应式系统”.“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码.例如,视图渲染中使用了数据,数据改变后,视图也会自动更新. 举个简单 ...
- 深入解析vue.js响应式原理与实现
vue.js响应式原理解析与实现.angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面.vue.js ...
- Vue的响应式原理
Vue的响应式原理 一.响应式的底层实现 1.Vue与MVVM Vue是一个 MVVM框架,其各层的对应关系如下 View层:在Vue中是绑定dom对象的HTML ViewModel层:在Vue中是实 ...
- 一探 Vue 数据响应式原理
一探 Vue 数据响应式原理 本文写于 2020 年 8 月 5 日 相信在很多新人第一次使用 Vue 这种框架的时候,就会被其修改数据便自动更新视图的操作所震撼. Vue 的文档中也这么写道: Vu ...
- vue系列---响应式原理实现及Observer源码解析(一)
_ 阅读目录 一. 什么是响应式? 二:如何侦测数据的变化? 2.1 Object.defineProperty() 侦测对象属性值变化 2.2 如何侦测数组的索引值的变化 2.3 如何监听数组内容的 ...
- 由自定义事件到vue数据响应
前言 除了大家经常提到的自定义事件之外,浏览器本身也支持我们自定义事件,我们常说的自定义事件一般用于项目中的一些通知机制.最近正好看到了这部分,就一起看了下自定义事件不同的实现,以及vue数据响应的基 ...
随机推荐
- codeforces 501 B Misha and Changing Handles 【map】
题意:给出n个名字变化,问一个名字最后变成了什么名字 先用map顺着做的,后来不对, 发现别人是将变化后的那个名字当成键值来做的,最后输出的时候先输出second,再输出first 写一下样例就好理解 ...
- GoldenGate V11.1数据复制限制
以下对goldengate数据复制的限制情况进行说明. 不支持文件等非结构化数据复制 GoldenGate依赖对于数据库日志的解析获取数据变化,因此只能支持数据库中的数据变化复制,无法支持文件等非结构 ...
- [洛谷P1726][codevs1332]上白泽慧音
题目大意:求一个有向图的最大强连通分量中点的个数,并输出这些点(字典序最小). 解题思路:裸的强连通分量. 数据小,求完强连通分量后排序+vector大小比较即可(vector有小于运算符). C++ ...
- caioj 1075 动态规划入门(中链式2:能量项链)(中链式dp总结)
我又总结了一种动归模型-- 这道题和上一道题很类似,都是给一个序列,然后相邻的元素可以合并 然后合并后的元素可以再次合并 那么就可以用这两道题类似的方法解决 简单来说就是枚举区间,然后枚举断点 加上断 ...
- caioj 1070 动态规划入门(二维一边推3:字符距离)(最长公共子序列拓展)
复制上一题总结 caioj 1069到1071 都是最长公共字序列的拓展,我总结出了一个模型,屡试不爽 (1) 字符串下标从1开始,因为0用来表示字符为空的情况,而不是第一个字符 (2) ...
- Android开发之Menu:OptionMenu(选项菜单)、ContextMenu(上下文菜单)、SubMenu(子菜单)
菜单的概念,现在已经很普及了.Windows系统.Mac.桌面版Linux.Java Swing等,都有可视化菜单.一.Android平台3种菜单 选项菜单(OptionMenu).上下文菜单(Co ...
- Unity Shader (四)顶点程序示例
1.在顶点函数中实现凸起效果 Shader "Custom/Example" { properties { _R(,))= //圆的半径,也是凸起的范围 _OX(,))= //x轴 ...
- Unity 调用 Android Native 方法(一) 获得Android系统音量
学习雷锋,好榜样,接下来的这一系类教程里,将通过unity来实现Android端的一些常用功能, 不需要在 Asset/Plugins/Android 目录下引用jar包或者aar包,这是重点. us ...
- CodeForces 337A Puzzles
Puzzles Time Limit: 1000ms Memory Limit: 262144KB This problem will be judged on CodeForces. Origina ...
- Httphelper头信息(ContentType)默认为text/html无懈可击
Httphelper头信息(ContentType)默认为text/html无懈可击转 http://www.sufeinet.com/thread-8623-1-1.html 我发现最近有几个网友提 ...