源码分析Vue.js的监听实现教程


Posted in Javascript onApril 23, 2017

前言

相信一说到监听,当然就离不了设计模式中鼎鼎大名的观察者模式。举个例子,你家后院着火了,可一定要等到烟雾很大火光很亮你才能发现啊,可是当你安装了一个火灾预警器,当发生火灾就立马能够通知到你了。这就是一个典型的观察者模式。当然也还有一些其他变种,比如发布/订阅(publish/subscribe)模式。

我们知道如果要将数据和视图关联起来,在数据变更的时候,同步视图,同理视图变更,数据也发生变化。vue.js是怎么实现这个的呢?下面我们来揭开它的神秘面纱。

demo:

<script src="../vue.js"> </script>
<div id="app">
 <p>
 {{ message }}
 </p>
 <input v-model="message">
</div>
<script type="text/javascript">
new Vue({
 el: '#app',
 data: {
 message: 'Hello Vue.js!'
 }
});
</script>




set: function reactiveSetter(newVal) {
 var value = getter ? getter.call(obj) : val;
 if (newVal === value) {
 return;
 }
 if (setter) {
 setter.call(obj, newVal);
 } else {
 val = newVal;
 }
 childOb = observe(newVal);
 dep.notify();
}

这段代码出现在解析data属性的时候,即调用Object.defineProperty方法配置data的属性。一旦属性发生变化,就notify发送广播。

Dep.prototype.notify = function () {
 // stablize the subscriber list first
 var subs = toArray(this.subs);
 for (var i = 0, l = subs.length; i < l; i++) {
 subs[i].update();
 }
};

notify 最终是周知subscribe(订阅者)更新,那么上面的数据变更就是发布者。 subscribe是Watcher这个类的实例化对象,在实例化的时候,会传入回调函数来执行update,vue弄了一个队列来执行watcher的更新函数,具体可参考源码。

Watcher.prototype.run = function () {
 ……
 if (value !== this.value || (isObject(value) || this.deep) && !this.shallow) {
 ……
 } else {
 this.cb.call(this.vm, value, oldValue);
 }
 }
 this.queued = this.shallow = false;
 }
 };

在Directive(指令)class中实例化了Watcher,_update函数负责来更新

var watcher = this._watcher = new Watcher(this.vm, this.expression, this._update, // callback
 {
 filters: this.filters,
 twoWay: this.twoWay,
 deep: this.deep,
 preProcess: preProcess,
 postProcess: postProcess,
 scope: this._scope
 });

在解析模板的时候会解析Directive,然后绑定,实例化watcher,这样模板-data就关联在一起了。

图片描述

源码分析Vue.js的监听实现教程

观察者模式

林林总总的mvc或者mvvm框架基本也都是利用了观察者模式,这个也非常有用,尤其在复杂的系统之中。

利用观察者模式,在典型的ajax应用中,回调的处理逻辑可以不跟请求耦合在一块,这样逻辑上也会更加清晰。如下是一个简单的发布/订阅模式的实现

var PubSub = {};
(function (q) {
 var topics = {}, subUid = -1;
 q.publish = function (topic) {
 if(!topics[topic]){
  return false;
 }

 var subscribers = topics[topic],
  len = subscribers ? subscribers.length : 0;

 while(len--){
  var args = Array.prototype.slice.call(arguments, 1);
  args.unshift(topic);
  subscribers[len].callback.apply(this, args);
 }
 return this;
 };

 q.subscribe = function (topic, callback) {
 if(!topics[topic]){
  topics[topic] = [];
 }

 var subuid = (++subUid).toString();

 topics[topic].push({
  token: subuid,
  callback: callback
 });

 return subuid;
 };

 q.unsubscribe = function (subid) {
 for(var k in topics){
  if(topics[k]){
  for(var i = 0, j = topics[k].length; i < j; i++){
   if(topics[k][i].token === subid){
   topics[k].splice(i, 1);
   return subid;
   }
  }
  }
 }
 return this;
 };
})(PubSub);

这就是一个简单的订阅发布系统,每注册一个订阅者,其实就是将其回调处理的callback保存在一个字典对象的数组中,字典对象的key值可以随意定义,只要与发布时的key对应起来就好。

怎么使用呢?

<script>
var messageLogger = function(){
 console.log(JSON.stringify(arguments));
 };

var subscription = PubSub.subscribe('/newMessage', messageLogger);
// {"0":"/newMessage","1":"hello world"}
PubSub.publish('/newMessage', 'hello world');

// {"0":"/newMessage","1":["test","a","b","c"]}
PubSub.publish('/newMessage', ['test', 'a', 'b', 'c']);

// {"0":"/newMessage","1":{"sender":"hello world","body":"hey man"}}
PubSub.publish('/newMessage', {
 sender: 'hello world',
 body: 'hey man'
});

PubSub.unsubscribe(subscription);

PubSub.publish('/newMessage', ['test', 'a', 'b', 'c'], 1);
</script>

最后一个将不会打印出来,因为已经取消订阅了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
基于jquery的修改当前TAB显示标题的代码
Dec 11 Javascript
Javascript数组的排序 sort()方法和reverse()方法
Jun 04 Javascript
jquery 选择器引擎sizzle浅析
Feb 06 Javascript
jquery在项目中做复选框时遇到的一些问题笔记
Nov 17 Javascript
jqeury-easyui-layout问题解决方法
Mar 24 Javascript
JavaScript中如何通过arguments对象实现对象的重载
May 12 Javascript
基于Bootstrap3表格插件和分页插件实例详解
May 17 Javascript
学习使用bootstrap的modal和carousel
Dec 09 Javascript
jQuery实现动态删除LI的方法
May 30 jQuery
javascript少儿编程关于返回值的函数内容
May 27 Javascript
JS与jQuery判断文本框还剩多少字符可以输入的方法
Sep 01 jQuery
JS表格的动态操作完整示例
Jan 13 Javascript
Bootstrap进度条与AJAX后端数据传递结合使用实例详解
Apr 23 #Javascript
springMVC + easyui + $.ajaxFileUpload实现文件上传注意事项
Apr 23 #Javascript
Node.js中的require.resolve方法使用简介
Apr 23 #Javascript
AngularJS之ionic 框架下实现 Localstorage本地存储
Apr 22 #Javascript
vue拦截器Vue.http.interceptors.push使用详解
Apr 22 #Javascript
vue+vuex+axio从后台获取数据存入vuex实现组件之间共享数据
Apr 22 #Javascript
Vue.use源码分析
Apr 22 #Javascript
You might like
PHP命名空间和自动加载类
2016/04/03 PHP
[原创]PHP正则匹配中英文、数字及下划线的方法【用户名验证】
2017/08/01 PHP
用javascript控制iframe滚动的代码
2007/04/10 Javascript
FileUpload 控件 禁止手动输入或粘贴的实现代码
2010/04/07 Javascript
javascrpt绑定事件之匿名函数无法解除绑定问题
2012/12/06 Javascript
Python脚本后台运行的几种方式
2015/03/09 Javascript
javascript实现详细时间提醒信息效果的方法
2015/03/11 Javascript
详解JavaScript函数
2015/12/01 Javascript
AngularJS基础 ng-if 指令用法
2016/08/01 Javascript
关于javascript原型的修改与重写(覆盖)差别详解
2016/08/31 Javascript
通过网页查看JS源码中汉字显示乱码的解决方法
2016/10/26 Javascript
jquery中$.fn和图片滚动效果实现的必备知识总结
2017/04/21 jQuery
Easy UI动态树点击文字实现展开关闭功能
2017/09/30 Javascript
利用JavaScript的%做隔行换色的实例
2017/11/25 Javascript
vue实现图片加载完成前的loading组件方法
2018/02/05 Javascript
vue awesome swiper异步加载数据出现的bug问题
2018/07/03 Javascript
通过jQuery学习js类型判断的技巧
2019/05/27 jQuery
Node.js API详解之 repl模块用法实例分析
2020/05/25 Javascript
JavaScript实现网页tab栏效果制作
2020/11/20 Javascript
[01:05:59]Mineski vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.22
2019/09/05 DOTA
python基础教程之元组操作使用详解
2014/03/25 Python
Python实现获取域名所用服务器的真实IP
2015/10/25 Python
python制作websocket服务器实例分享
2016/11/20 Python
详解python使用递归、尾递归、循环三种方式实现斐波那契数列
2018/01/16 Python
Django使用httpresponse返回用户头像实例代码
2018/01/26 Python
python config文件的读写操作示例
2019/09/27 Python
美国排名第一的泳池用品直接来源:In The Swim
2019/09/23 全球购物
荣耀俄罗斯官网:HONOR俄罗斯
2020/10/31 全球购物
大学在校生求职信范文
2013/11/21 职场文书
2014年园林绿化工作总结
2014/12/11 职场文书
销售工作决心书
2015/02/04 职场文书
春晚观后感
2015/06/11 职场文书
旅游投诉信范文
2015/07/02 职场文书
2016廉洁从业学习心得体会
2016/01/19 职场文书
Python中os模块的简单使用及重命名操作
2021/04/17 Python
雄兵连:第三季先行图公开,天使恶魔联合,银河之力的新力量
2021/06/11 国漫