vue-双向响应数据底层原理分析
总所周知,vue的一个大特色就是实现了双向数据响应,数据改变,视图中引用该数据的部分也会自动更新
一.双向数据绑定基本思路
“数据改变,视图中引用该数据的部分也会自动更新“,从这句话,我们可以分析出以下几点:
- 数据要绑定监听事件
- 视图要绑定更新事件
- 监听事件会触发更新事件
vue的双向数据响应也是基于以上思路开发的
二.vue基于数据劫持+发布订阅模式实现双向绑定
2.1数据劫持
利用Object.defineProperty深度遍历每个响应数据,给每个响应数据和其子属性添加get和set方法.
Object.defineProperty的get方法在数据被调用时执行.所以get()用于收集依赖,所谓的依赖就是使用当前响应数据的dom
Object.defineProperty的set方法在数据修改时被执行.所以set()方法用于通知监听事件,我的数据更新了defaultReative(data,key,value)
defaultReative(data,key,value){
//先去做递归
this.observer(value);
//给每个响应数据的get方法上都添加一个dep实例
var dep = new Dep();
//数据劫持
Object.defineProperty(data,key,{
get(){
/*在渲染html的时候会调用响应数据,也就执行了get方法;watcher实例是订阅者,渲染时会给当前dom初始化一个watcher实例,也就是给当前的dom添加了订阅者,
* 订阅者内部会有更新dom的方法,数据变化的时候,发布者通知订阅者,订阅者内部去调用更新dom的方法.
* watcher内部有dep.target赋值操作,赋值为watcher本身,
这样才能确保有我们收集的依赖就是调用了当前响应数据的订阅者.
*/
Dep.target && dep.addDep(Dep.target);
return value;
},
set(newVal){
if(newVal == value)return;
//赋值操作 当外部设置data身上的某个属性的时候 将新值赋值给旧值
value = newVal;
dep.notify();
}
})
}
dep是发布订阅模式的一环,会在下面讲解
2.2发布订阅模式
发布—订阅模式可以广泛应用于异步编程中,这是一种替代传递回调函数的方案.这种模式让一个对象不在显示地调用另一个对象的接口,实现了多个对象松耦合联系在一起.
只需要订阅感兴趣的事件发生点,当事件执行时就会通知给所有订阅者.
一个简单的发布-订阅模式的实现主要是以下三点内容:
- 指定好发布者;
- 发布者有一个缓存列表,里面存放了回调函数,以便发布后通知订阅者;
- 发布消息的时候遍历缓存列表,依次触发订阅者的回调
在vue中dep就类似发布者,下面是dep的实现
class Dep{
constructor(){
this.deps = [];
}
addDep(dep){
this.deps.push(dep);
}
notify(){
this.deps.forEach((item)=>{
item.update();
})
}
}
我们给每一个响应数据get()内部添加一个deps列表,列表内部就是调用该响应数据的订阅者,
我们给每一个响应数据set()内部添加一个dep.notify(),这个方法去通知订阅者调用内部的更新函数,更新视图
defaultReative(data,key,value){
//先去做递归
this.observer(value);
//给每个响应数据的get方法上都添加一个dep实例
var dep = new Dep();
//数据劫持
Object.defineProperty(data,key,{
get(){
/*在渲染html的时候会调用响应数据,也就执行了get方法;watcher实例是订阅者,渲染时会给当前dom初始化一个watcher实例,也就是给当前的dom添加了订阅者,
* 订阅者内部会有更新dom的方法,数据变化的时候,发布者通知订阅者,订阅者内部去调用更新dom的方法.
* watcher内部有dep.target赋值操作,赋值为watcher本身,
这样才能确保有我们收集的依赖就是调用了当前响应数据的订阅者
*/
Dep.target && dep.addDep(Dep.target);
return value;
},
set(newVal){
if(newVal == value)return;
//赋值操作 当外部设置data身上的某个属性的时候 将新值赋值给旧值
value = newVal;
dep.notify();
}
})
}
watcher
watcher类似订阅者,他会和dom去做关联.watcher内部有一个update方法,用于更新dom节点.
watcher也会跟Dep做关联,在constructor中,调用dep.target=this,目的是让Dep指向当前的watcher.响应数据对应的订阅者不同,每个响应数据收集到对应的依赖就需要指定this,因为在渲染dom的时候,我们会给每个dom节点都初始化一个watcher实例,this就是这个watcher实例
下面是一个watcher方法的实现
//监听数据的变化
class Watcher{
constructor(vm,exp,callback){
this.$vm = vm;
this.$exp = exp;
this.callback = callback;
//给dep添加了一个静态属性target,值是watcher自己
Dep.target = this;
//触发属性的getter方法
this.$vm[this.$exp];
Dep.target = null;
}
//在index.js中一旦数据有变动会触发dep.notify方法,
//nodity会遍历dep中收集的watcher,调用watcher中的update方法,在update方法中执行,
//这里(compile)传递给watcher的方法(updateFn)更新视图
update(){
//视图更新
this.callback.call(this.$vm,this.$vm[this.$exp])
}
}
在渲染dom的时候,将dom方法的更新函数传递给watcher做回调
update(el,vm,exp,type){
//为了其他的一些指令需要的一些公共的逻辑在这个方法里面编写
//比如这里是text指令,updateFn=textupdate
var updateFn = this[type+'update'];
//如果textupdate方法存在,就去执行textupdate方法
updateFn && updateFn(el,vm[exp]);
//给每个节点都添加上监听
//添加监听数据的订阅者(给watcher传递数据和回调),在index.js中一旦数据有变动会触发dep.notify方法,
//nodity会遍历dep中收集的watcher,调用watcher中的update方法,在update方法中执行,
//这里(compile)传递给watcher的方法(updateFn)更新视图
new Watcher(vm,exp,(value)=>{
updateFn && updateFn(el,value);
});
}
总结.
vue在编译html模版的时候,会给每个dom节点初始化watcher订阅者,把该节点的更新函数传递给watcher类中
vue在初始化时会深度遍历每一个响应属性,给每个响应属性添加上get和set方法,当html渲染时用到了响应数据就去调用get方法得到数据,同时get内部通过依赖收集知道了调用的订阅者都有那些;
在修改响应数据时会调用set方法,在方法内部通过dep.notify通知订阅者,订阅者去调用update更新dom节点
vue-双向响应数据底层原理分析的更多相关文章
- 梳理vue双向绑定的实现原理
Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后 ...
- Vue双向绑定的实现原理系列(四):补充指令解析器compile
补充指令解析器compile github源码 补充下HTML节点类型的知识: 元素节点 Node.ELEMENT_NODE(1) 属性节点 Node.ATTRIBUTE_NODE(2) 文本节点 N ...
- Vue双向绑定的实现原理系列(一):Object.defineproperty
了解Object.defineProperty() github源码 Object.defineProperty()方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象. ...
- Vue双向绑定的实现原理系列(三):监听器Observer和订阅者Watcher
监听器Observer和订阅者Watcher 实现简单版Vue的过程,主要实现{{}}.v-model和事件指令的功能 主要分为三个部分 github源码 1.数据监听器Observer,能够对数据对 ...
- HashMap底层原理分析(put、get方法)
1.HashMap底层原理分析(put.get方法) HashMap底层是通过数组加链表的结构来实现的.HashMap通过计算key的hashCode来计算hash值,只要hashCode一样,那ha ...
- vue双向绑定(数据劫持+发布者-订阅者模式)
参考文献:https://www.cnblogs.com/libin-1/p/6893712.html 实现mvvm主要包含两个方面,数据变化更新视图,视图变化更新数据. 关键点在于data如何更新v ...
- Vue双向绑定的实现原理及简单实现
vue数据双向绑定原理 vue数据双向绑定是通过(数据劫持)+(发布者-订阅者模式)的方式来实现的,而所谓的数据劫持就是通过Object.defineProperty() 来实现的,所谓的Obje ...
- Android应用程序组件Content Provider在应用程序之间共享数据的原理分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6967204 在Android系统中,不同的应用 ...
- JMM和Volatile底层原理分析
JMM和volatile分析 1.JMM:Java Memory Model,java线程内存模型 JMM:它是一个抽象的概念,描述的是线程和内存间的通信,java线程内存模型和CPU缓存模型类似,它 ...
随机推荐
- AJ学IOS(38)UI之核心动画简介
AJ分享,必须精品 核心动画(简介) Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍.也就是说,使用少量的代码就 ...
- kworkerds 挖矿木马简单分析及清理
公司之前的开发和测试环境是在腾讯云上,部分服务器中过一次挖矿木马 kworkerds,本文为我当时分析和清理木马的记录,希望能对大家有所帮助. 现象 top 命令查看,显示 CPU 占用 100%,进 ...
- delphi中DateTimePicker控件同时输入日期和时间
将DateTimePicker的Format属性中加入日期格式设成 'yyyy-MM-dd HH:mm',注意大小写 , 将kind设置为dtkTime即可,可以在每次Form onShow时将Dat ...
- Xshell远程连接Linux系统
一般来说我们连接Linux,会使用到一些远程连接工具 比如:Xshell和Xftp Xshell:远程连接linux系统 Xftp:远程在Linux系统中上传或下载文件 Xshell和Xftp百度云链 ...
- 基于canvas的画板
最近重新在看Html5&CSS3的知识,看到canvas的时候,想到了以前在学校学计算机图形学时做过的画图实验,于是想,可以基于html5和css3来做一款画板,经过1天的努力,完成了画板的一 ...
- 运行一个nodejs服务,先发布为deployment,然后创建service,让集群外可以访问
问题来源 海口-老男人 17:42:43 就是我要运行一个nodejs服务,先发布为deployment,然后创建service,让集群外可以访问 旧报纸 17:43:35 也就是 你的需求为 一个a ...
- Python常用库-Psutil
背景 介绍一个处理进程的实用工具,这个是一个第三方库.应用主要有类似ps.cd.top,还有查看硬盘.内存使用情况等. 推荐的理由主要有 2 个,第一个是跨平台的,不管是OSX.Centos.Wind ...
- ApiPost如何在预执行脚本里添加请求参数?
ApiPost V3引入了预执行脚本和后执行脚本的概念,详细可以通过链接:<ApiPost的预执行脚本和后执行脚本>了解学习更多.本文主要介绍如何在预执行脚本里增加请求参数. 使用场景 我 ...
- 在项目中部署redis的读写分离架构(包含节点间认证口令)
#### 在项目中部署redis的读写分离架构(包含节点间认证口令) ##### 1.配置过程 --- 1.此前就是已经将redis在系统中已经安装好了,redis utils目录下,有个redis ...
- Windows API 中 OVERLAPPED 结构体 初始化
出处:https://github.com/microsoft/Windows-classic-samples/blob/1d363ff4bd17d8e20415b92e2ee989d615cc0d9 ...