源码分析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的loading 加载提示效果实现代码
Sep 01 Javascript
jQuery操作select的实例代码
Jun 14 Javascript
javascript检测页面是否缩放的小例子
May 16 Javascript
JavaScript中使用arguments获得函数传参个数实例
Aug 27 Javascript
Jquery搜索父元素操作方法
Feb 10 Javascript
freemarker判断对象是否为空的方法
Aug 13 Javascript
浅析创建javascript对象的方法
May 13 Javascript
JS简单实现移动端日历功能示例
Dec 28 Javascript
浅谈vue中使用图片懒加载vue-lazyload插件详细指南
Oct 23 Javascript
vue2.0+vuex+localStorage代办事项应用实现详解
May 31 Javascript
简述vue状态管理模式之vuex
Aug 29 Javascript
JS对象属性的检测与获取操作实例分析
Mar 17 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
《星际争霸II》全新指挥官斯台特曼现已上线
2020/03/08 星际争霸
PHP 杂谈《重构-改善既有代码的设计》之五 简化函数调用
2012/05/07 PHP
PHP对象相互引用的内存溢出实例分析
2014/08/28 PHP
浅谈laravel orm 中的一对多关系 hasMany
2019/10/21 PHP
Javascript 键盘keyCode键码值表
2009/12/24 Javascript
js相册效果代码(点击创建即可)
2013/04/16 Javascript
javascript中2个感叹号的用法实例详解
2014/09/04 Javascript
JS实现点击按钮自动增加一个单元格的方法
2015/03/09 Javascript
JavaScript中的函数嵌套使用
2015/06/04 Javascript
JS实现新浪微博效果带遮罩层的弹出框代码
2015/10/12 Javascript
纯css下拉菜单 无需js
2016/08/15 Javascript
nodejs+websocket实时聊天系统改进版
2017/05/18 NodeJs
jQuery滚动插件scrollable.js用法分析
2017/05/25 jQuery
Angular限制input框输入金额(是小数的话只保留两位小数点)
2017/07/13 Javascript
bootstrap多层模态框滚动条消失的问题
2017/07/21 Javascript
js字符串倒序的实例代码
2018/11/30 Javascript
JS中call()和apply()的功能及用法实例分析
2019/06/28 Javascript
解决vue watch数据的方法被调用了两次的问题
2020/11/07 Javascript
JavaScript中常用的3种弹出提示框(alert、confirm、prompt)
2020/11/10 Javascript
零基础写python爬虫之抓取百度贴吧并存储到本地txt文件改进版
2014/11/06 Python
Python中实现常量(Const)功能
2015/01/28 Python
python 异常处理总结
2016/10/18 Python
Python 内置函数complex详解
2016/10/23 Python
Python 获取当前所在目录的方法详解
2017/08/02 Python
Python内置模块logging用法实例分析
2018/02/12 Python
PyCharm 配置远程python解释器和在本地修改服务器代码
2019/07/23 Python
对Python中一维向量和一维向量转置相乘的方法详解
2019/08/26 Python
html5各种页面切换效果和模态对话框用法总结
2014/12/15 HTML / CSS
留学自荐信写作方法
2014/01/27 职场文书
教师职业道德事迹材料
2014/08/18 职场文书
幼儿园小班见习报告
2014/10/31 职场文书
2014年学校办公室工作总结
2014/12/19 职场文书
教师党员个人总结
2015/02/10 职场文书
关于分班的感言
2015/08/04 职场文书
浅谈Python中的函数(def)及参数传递操作
2021/05/25 Python
分析并发编程之LongAdder原理
2021/06/29 Java/Android