源码分析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 相关文章推荐
js 获取子节点函数 (兼容FF与IE)
Apr 18 Javascript
javascript之querySelector和querySelectorAll使用说明
Oct 09 Javascript
日期处理的js库(迷你版)--自建js库总结
Nov 21 Javascript
从js向Action传中文参数出现乱码问题的解决方法
Dec 29 Javascript
深入理解JavaScript系列(37):设计模式之享元模式详解
Mar 04 Javascript
jQuery判断元素上是否绑定了指定事件的方法
Mar 17 Javascript
javascript框架设计之类工厂
Jun 23 Javascript
使用JavaScript实现连续滚动字幕效果的方法
Jul 07 Javascript
浅谈javascript 函数表达式和函数声明的区别
Jan 05 Javascript
MVC+jQuery.Ajax异步实现增删改查和分页
Dec 22 Javascript
vue通过cookie获取用户登录信息的思路详解
Oct 30 Javascript
vue elementui el-form rules动态验证的实例代码详解
May 23 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禁止页面缓存的代码
2011/10/23 PHP
PHP中require和include路径问题详解
2014/12/25 PHP
Laravel框架中VerifyCsrfToken报错问题的解决
2017/08/30 PHP
实例讲解PHP表单验证功能
2019/02/15 PHP
JavaScript 嵌套函数指向this对象错误的解决方法
2010/03/15 Javascript
JavaScript格式化日期时间的方法和自定义格式化函数示例
2014/04/04 Javascript
深入分析JQuery和JavaScript的异同
2014/10/23 Javascript
Vue.js快速入门实例教程
2016/10/15 Javascript
jQuery选择器实例应用
2017/01/05 Javascript
Javascript 一些需要注意的细节(必看篇)
2017/07/08 Javascript
Vue2 Vue-cli中使用Typescript的配置详解
2017/07/24 Javascript
JS 中使用Promise 实现红绿灯实例代码(demo)
2017/10/20 Javascript
如何在项目中使用log4.js的方法步骤
2019/07/16 Javascript
JavaScript简单编程实例学习
2020/02/14 Javascript
关于vue 结合原生js 解决echarts resize问题
2020/07/26 Javascript
SublimeText 2编译python出错的解决方法(The system cannot find the file specified)
2013/11/27 Python
Python中用Descriptor实现类级属性(Property)详解
2014/09/18 Python
Python操作MySQL数据库9个实用实例
2015/12/11 Python
Python多线程爬虫实战_爬取糗事百科段子的实例
2017/12/15 Python
python3+PyQt5自定义视图详解
2018/04/24 Python
详解PyCharm安装MicroPython插件的教程
2019/06/24 Python
Python对接 xray 和微信实现自动告警
2019/09/17 Python
Python实现字符串中某个字母的替代功能
2019/10/21 Python
在python3中实现更新界面
2020/02/21 Python
Python线程threading模块用法详解
2020/02/26 Python
OpenCV读取与写入图片的实现
2020/10/13 Python
css3的过滤效果简单实例
2016/08/03 HTML / CSS
荷兰网上买鞋:MooieSchoenen.nl
2017/09/12 全球购物
优秀士兵个人事迹材料
2014/01/19 职场文书
小加工厂管理制度
2014/01/21 职场文书
英语生日邀请函
2014/01/23 职场文书
教师个人剖析材料
2014/02/05 职场文书
2015年反腐倡廉工作总结
2015/05/14 职场文书
2015党建工作简报
2015/07/21 职场文书
Springboot/Springcloud项目集成redis进行存取的过程解析
2021/12/04 Redis
Python中super().__init__()测试以及理解
2021/12/06 Python