监听器Observer和订阅者Watcher

实现简单版Vue的过程,主要实现{{}}、v-model和事件指令的功能

主要分为三个部分

github源码

 1.数据监听器Observer,能够对数据对象的所有属性进行监听;实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性2.Watcher将数据监听器和指令解析器连接起来,数据的属性变动时,执行指令绑定的相应回调函数,1.如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。3.指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。=

监听器Observer

Observer是一个数据监听器,核心是前面一直谈论的Object.defineProperty(),
对所有属性监听,利用递归来遍历所有的属性值,对其进行Object.defineProperty()操作:

    function definReactive(data,key,val){observers(val);//递归所有子属性Object.defineProperty(data,key,{enumerable:true,configurable:true,get:function(){console.log('属性'+key+'执行get');return val;},set:function(newVal){val = newVal;console.log('属性:'+key+'以及被监听,现在值为:'+newVal.toString());}})}function observers(data){if(!data || typeof data!='object'){return;}Object.keys(data).forEach(function(key){definReactive(data,key,data[key]);})}var library = {book1:{name:''},book2:''}observers(library);library.book1.name = 'vue书籍';library.book2 = '没有书';//属性book1执行get//属性:name以及被监听,现在值为:vue书籍//属性:book2以及被监听,现在值为:没有书

接下来创建一个收集所有订阅者的订阅器Dep,阅器Dep主要负责收集订阅者,然后再属性变化的时候执行对应订阅者的更新函数,
再改写一下订阅器Observer,创建一个observer.js:

    function Observe(data){this.data = data;this.walk(data);}Observe.prototype = {walk:function(data){var self = this;Object.keys(data).forEach(function(key) {self.defineReactive(data, key, data[key]);});},defineReactive:function(data,key,val){observers(val);//递归所有子属性var dep = new Dep();Object.defineProperty(data,key,{enumerable:true,configurable:true,get:function(){if(是否需要添加订阅者){dep.addSub(Watcher);//在这里添加一个订阅者}console.log('属性'+key+'执行get');return val;},set:function(newVal){if(val === newVal){return;}val = newVal;dep.notify();//如果数据变化,通知所有订阅者console.log('属性:'+key+'以及被监听,现在值为:'+newVal.toString());}})}}function observers(data){if(!data || typeof data!='object'){return;}return new Observe(data);}/**Dep:创建一个可以容纳订阅者的消息订阅器* **/function Dep(){this.subs = [];}Dep.prototype = {addSub:function(sub){//添加订阅者this.subs.push(sub);},notify:function(){//通知订阅者this.subs.forEach(function(sub){sub.update();})}}可以看出,订阅器Dep,添加一个订阅者是在Object.defineProperty()的get里面,这是为了让Watcher初始化进行触发,因此要判断是不是需要添加订阅者,后面解释。在set里面,如果数据变化,就会通知所有的订阅者,订阅者就会去执行对应的更新的函数以上,一个完整的订阅器完成。

订阅者Watcher

Watcher在初始化的时候要将自己添加进订阅者Dep中,如何做到:

已经知道监听器Observer是在get函数执行了添加订阅者Wather的操作的,

所以我们只要在订阅者Watcher初始化的时候触发对应的get函数,去执行添加订阅者操作即可,

那要如何触发get的函数:

只要获取对应的属性值就可以触发了,核心原因就是因为我们使用了Object.defineProperty()进行数据监听。

注意:

我们只要在订阅者Watcher初始化的时候才需要添加订阅者,所以需要做一个判断操作,

因此可以在订阅器上做一下手脚:在Dep.target上缓存下订阅者,添加成功后再将其去掉就可以了。

创建一个watcher.js

    function Watcher(vm,exp,cb){this.cb = cb;this.vm = vm;this.exp = exp;this.value = this.get();//将自己添加到订阅器的操作}Watcher.prototype = {update:function () {this.run();},run:function () {var value = this.vm.data[this.exp];var oldVal = this.value;if(value != oldVal){this.value = value;this.cb.call(this.vm,value,oldVal);}},get:function () {Dep.target = this;//缓存自己var value = this.vm.data[this.exp];//强制执行监听器observer里的Object.defineProperty()里的get函数Dep.target = null;//释放自己return value;}}

再调整下observer.js的defineReactive函数里的get操作:

    defineReactive:function(data,key,val){observers(val);//递归所有子属性var dep = new Dep();Object.defineProperty(data,key,{enumerable:true,configurable:true,get:function(){if(Dep.target){dep.addSub(Dep.target);//在这里添加一个订阅者}console.log('属性'+key+'执行get');return val;},set:function(newVal){if(val === newVal){return;}val = newVal;dep.notify();//如果数据变化,通知所有订阅者console.log('属性:'+key+'以及被监听,现在值为:'+newVal.toString());}})}//Dep加个target属性function Dep(){this.subs = [];this.target = null;}

简单版的Watcher设计好了,
只要将Observer和Watcher关联起来,就可以实现一个简单的双向绑定数据了。
这里没有还没有设计解析器Compile,所以对于模板数据我们都进行写死处理:
模板有个节点,id为name,双向数据绑定的变量name,这里大框号暂时没有用:

<body> <h1 id="name">{{name}}</h1>
</body>

selVue.js 关联Observer和Watcher

    function SelfVue(data,el,exp){this.data = data;observers(data);el.innerHTML = this.data[exp];//初始化模板数据的值new Watcher(this,exp,function(value){el.innerHTML = value;});return this;}

页面上实现双向数据绑定:

<h1 id="name">{{name}}</h1><script src="js/observer.js"></script><script src="js/watcher.js"></script><script src="js/selfVue.js"></script><script>var ele = document.querySelector('#name');var self_Vue = new SelfVue({name:'第一次显示数据'},ele,'name');window.setTimeout(function(){console.log('值变了');self_Vue.data.name = '重新赋值了';},2000);</script>

打开页面,可以看到页面刚开始显示了是“第一次显示数据”,过了2s后就变成“重新赋值了”了。到这里,完成了一部分

注意:赋值的时候是 self_Vue.data.name = '重新赋值了',但是希望是 self_Vue.name = '重新赋值了',
需要在new SelVue的时候做个代理,让访问self_Vue的属性代理为访问self_Vue.data的属性,
实现原理还是使用Object.defineProperty()对属性再包一层,

修改selVue.js:

    function SelfVue(data,el,exp){var self = this;this.data = data;Object.keys(data).forEach(function (key) {self.proxyKeys(key);//绑定代理属性});observers(data);el.innerHTML = this.data[exp];//初始化模板数据的值new Watcher(this,exp,function(value){el.innerHTML = value;});return this;}SelfVue.prototype = {proxyKeys:function(key){var self = this;Object.defineProperty(this,key,{enumerable:false,configurable:true,get:function proxyGetter(){return self.data[key];},set:function proxySetter(newVal){self.data[key] = newVal;}})}}//这下我们就可以直接通过self_Vue.name = '重新赋值了'的形式来进行改变模板数据。

至此监听器Observer和订阅者Watcher功能基本完成,后面再加上指令解析器compile的功能!

系列文章的目录:

Vue双向绑定的实现原理系列(一):Object.defineproperty
Vue双向绑定的实现原理系列(二):设计模式
Vue双向绑定的实现原理系列(三):监听器Observer和订阅者Watcher
Vue双向绑定的实现原理系列(四):补充指令解析器compile

Vue双向绑定的实现原理系列(三):监听器Observer和订阅者Watcher相关推荐

  1. 梳理vue双向绑定的实现原理

    Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后 ...

  2. “约见”面试官系列之常见面试题第四十篇之双向绑定以及实现原理(建议收藏)

    目录 MVC模式 MVVM模式 双向绑定原理 1.实现一个Observer 2.实现一个Watcher 3.实现一个Compile 4.实现一个MVVM 最后写一个html测试一下我们的功能 MVC模 ...

  3. vue双向绑定原理及实现

    vue双向绑定原理及实现 一.MVC模式 二.MVVM模式 三.双向绑定原理 1.实现一个Observer 2.实现一个Watcher 3.实现一个Compile 4.实现一个MVVM 四.最后写一个 ...

  4. 前端技术栈:Vue 双向绑定

    目 录 MVVM模式 Vue 数据双向绑定原理 通过一个"加<XXX>"的例子来理解 思路分析 实现双向绑定 实现最简单的双向绑定 Vue 代码实现 1. 实现 obs ...

  5. 记一次对vue双向绑定的理解

    之前有看过一次vue双向绑定原理实现相关的博客,看得似懂非懂的,然后也就搁浅了. 昨天脑海里又突然燃起了要不这块搞懂的冲动,于是乎又开始了一轮博客轰炸,综合研究了多位大神写得关于vue双向绑定的实现原 ...

  6. vue 双向绑定 getter 和 setter

    它的每个属性都有两个相对应的get和set方法,我觉的这是多此一举的,于是去网上查了查Vue双向绑定的实现原理,才发现它和Angular.js双向绑定的实现原理完全不同,Angular是用的数据脏检测 ...

  7. Vue双向绑定是怎么实现的?

    用了Vue长达2年,如果自己去实现一个双向绑定,我可能一个字母都写不出来,是时候探究一下了. 先看data里某对象的输出 data() {return {pagination: {layout: 'p ...

  8. Vue双向绑定:原理篇(详细)

    文章目录 前言 什么是响应式 数据劫持 发布者-订阅者模式 模式简介 发布者 Observer 订阅器 dep 订阅者 Watcher 整体流程 初始化data data变为响应式数据 解析模板 收集 ...

  9. vue双向绑定的原理

    之前我有个学生在面试的时候,面试官问vue的双向绑定如何实现?学生说用v-module实现.又问那么双向绑定的原理是什么?就回答不上来了,这个offer工资在18k左右,其他问题都回答上来了,如果这个 ...

  10. 深入理解vue.js双向绑定的实现原理

    vue.js是MVVM(模型到视图和视图到模型)结构的,同类的还有AngularJs:至于MVC.MVP.MVVM的比较网上已经有很多了,这样不再重复.这篇文章将给大家深入的介绍vue.js双向绑定的 ...

最新文章

  1. html背景过大,html – CSS – 背景大小:封面太大了
  2. 你应该知道的 RPC 原理
  3. jzoj1295,P1607-轻轨(庙会班车)【贪心,线段树】
  4. 用js和jQuery做轮播图
  5. ubuntu 2018 apt 代理proxy设置
  6. STM32F103系统滴答计时器
  7. OpenShift 4 - Fedora CoreOS (6) - 用rpm-ostree安装软件、升级回滚CoreOS
  8. 远哥跟你说 Spring的 classpath 通配符加载配置文件
  9. 基于CarMaker的C-NCAP主动安全系统试验仿真(一)
  10. 在计算机上最常用的英语单词,计算机常用英语单词
  11. 1015 计算摄氏温度值
  12. html静态页面图书馆管理,静态页面管理
  13. 【微信video视频播放】video标签
  14. 量子计算机采用超导技术吗,华人学者一作论文发现不寻常超导体,或可作为量子计算机的“硅”...
  15. 双臂UR5的Gazebo配置
  16. 网络视频录像机地址无法修改如何解决
  17. 衢州学院的计算机专业是本科还是专科,浙江本科对比:选择衢州学院还是温州大学好?...
  18. 资本持续“押注”激光雷达,技术方案组合“百花齐放”抢市场
  19. ubuntu14.04安装gnu/emacs24
  20. 购买《哈利波特》书方案

热门文章

  1. Android的JNI【实战教程】3⃣️--Java调用C代码
  2. 技术人真的能做一辈子技术么?
  3. 你以为只有菜鸟求职碰壁,其实还有...
  4. 用户名_网络安全与防御-OpenSSH用户名枚举及其检测方法
  5. python render_python:玫瑰图展示2020年1-3月轿车销量前十及占比(pyecharts)
  6. html 文本框 p,Javascript实现HTML表单form多个HttpPost请求
  7. python-学生管理系统--5 统计学生总人数功能
  8. pyqt5 登录窗口调用主窗口
  9. python 图片 变清晰_python模糊图片过滤的方法
  10. thinkcmf 配置初始化加载模版