源码分析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 相关文章推荐
Jvascript学习实践案例(开发常用)
Jun 25 Javascript
jQuery+CSS 半开折叠效果原理及代码(自写)
Mar 04 Javascript
JavaScript加强之自定义callback示例
Sep 21 Javascript
jquery将一个表单序列化为一个对象的方法
Jan 03 Javascript
JQuery中DOM事件冒泡实例分析
Jun 13 Javascript
javascript如何定义对象数组
Jun 07 Javascript
基于jQuery实现的查看全文功能【实用】
Dec 11 Javascript
通过jquery获取上传文件名称、类型和大小的实现代码
Apr 19 jQuery
JQuery样式与属性设置方法分析
Dec 07 jQuery
vue实现购物车加减
May 30 Javascript
前端开发基础javaScript的六大作用
Aug 06 Javascript
vue在图片上传的时候压缩图片
Nov 18 Vue.js
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
雄兵连三大错觉:凯莎没了,凉冰阵亡了,华烨觉得自己又行了
2020/04/09 国漫
一个目录遍历函数
2006/10/09 PHP
php遍历树的常用方法汇总
2015/06/18 PHP
PHP获取文件扩展名的方法实例总结
2017/06/10 PHP
js判断变量是否空值的代码
2008/10/26 Javascript
javascript 二维数组的实现与应用
2010/03/16 Javascript
基于JQuery的抓取博客园首页RSS的代码
2011/12/01 Javascript
html+css+js实现xp window界面及有关功能
2013/03/26 Javascript
javascript实现点击单选按钮链接转向对应网址的方法
2015/08/12 Javascript
基于jquery实现人物头像跟随鼠标转动
2015/08/23 Javascript
浅析JavaScript 调试方法和技巧
2015/10/22 Javascript
jQuery实现的自动加载页面功能示例
2016/09/04 Javascript
微信小程序 教程之条件渲染
2016/10/18 Javascript
vue2.0获取自定义属性的值
2017/03/28 Javascript
vue写一个组件
2018/04/09 Javascript
vue2.0实现音乐/视频播放进度条组件
2018/06/06 Javascript
vue日历/日程提醒/html5本地缓存功能
2019/09/02 Javascript
Python的gevent框架的入门教程
2015/04/29 Python
Python实现的计数排序算法示例
2017/11/29 Python
numpy matrix和array的乘和加实例
2018/06/28 Python
python pandas写入excel文件的方法示例
2019/06/25 Python
使用python来调用CAN通讯的DLL实现方法
2019/07/03 Python
Python实现线性插值和三次样条插值的示例代码
2019/11/13 Python
基于python 凸包问题的解决
2020/04/16 Python
python实现扑克牌交互式界面发牌程序
2020/04/22 Python
如何基于python把文字图片写入word文档
2020/07/31 Python
Python txt文件常用读写操作代码实例
2020/08/03 Python
Python包资源下载路径报404解决方案
2020/11/05 Python
Django解决frame拒绝问题的方法
2020/12/18 Python
HTML5标签大全
2016/11/23 HTML / CSS
加拿大专业美发产品购物网站:Chatters
2021/02/28 全球购物
简述Linux文件系统通过i节点把文件的逻辑结构和物理结构转换的工作过程
2012/04/17 面试题
LINUX下线程,GDI类的解释
2016/12/14 面试题
分公司总经理岗位职责
2014/07/30 职场文书
大学运动会通讯稿
2015/07/18 职场文书
Python中如何处理常见报错
2022/01/18 Python