源码分析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 URL参数判断,确定菜单样式
May 31 Javascript
jquery实现心算练习代码
Dec 06 Javascript
JavaScript实现维吉尼亚(Vigenere)密码算法实例
Nov 22 Javascript
jQuery晃动层特效实现方法
Mar 09 Javascript
javascript包装对象实例分析
Mar 27 Javascript
javascript带回调函数的异步脚本载入方法实例分析
Jul 02 Javascript
angularJs中$scope数据序列化的实例
Sep 30 Javascript
electron制作仿制qq聊天界面的示例代码
Nov 26 Javascript
微信小程序入口场景的问题集合与相关解决方法
Jun 26 Javascript
ElementUI Tag组件实现多标签生成的方法示例
Jul 08 Javascript
原理深度解析Vue的响应式更新比React快
Apr 04 Javascript
Vue 同步异步存值取值实现案例
Aug 05 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
海贼王:最美的悬赏令!
2020/03/02 日漫
php中拷贝构造函数、赋值运算符重载
2012/07/25 PHP
如何使用php脚本给html中引用的js和css路径打上版本号
2015/11/18 PHP
PHP中ID设置自增后不连续的原因分析及解决办法
2016/08/21 PHP
Laravel给生产环境添加监听事件(SQL日志监听)
2017/06/19 PHP
xtree.js 代码
2007/03/13 Javascript
Javascript 判断Flash是否加载完成的代码
2010/04/12 Javascript
javascript 树形导航菜单实例代码
2013/08/13 Javascript
JS+JSP checkBox 全选具体实现
2014/01/02 Javascript
node.js中的fs.lchown方法使用说明
2014/12/16 Javascript
jquery插件qrcode在线生成二维码
2015/04/26 Javascript
JavaScript探测CSS动画是否已经完成的方法
2016/08/30 Javascript
Vue.js每天必学之过滤器与自定义过滤器
2016/09/07 Javascript
AngularJS操作键值对象类似java的hashmap(填坑小结)
2016/11/12 Javascript
jQuery基于xml格式数据实现模糊查询及分页功能的方法
2016/12/25 Javascript
jQuery使用unlock.js插件实现滑动解锁
2017/04/04 jQuery
JS获取鼠标坐标并且根据鼠标位置不同弹出不同内容
2017/06/12 Javascript
JS数组交集、并集、差集的示例代码
2017/08/23 Javascript
VUE基于NUXT的SSR 服务端渲染
2018/11/30 Javascript
解决echarts 一条柱状图显示两个值,类似进度条的问题
2020/07/20 Javascript
微信小程序使用前置摄像头拍照
2020/10/22 Javascript
pygame学习笔记(2):画点的三种方法和动画实例
2015/04/15 Python
Tensorflow 自带可视化Tensorboard使用方法(附项目代码)
2018/02/10 Python
OpenCV+python手势识别框架和实例讲解
2018/08/03 Python
计算机二级python学习教程(3) python语言基本数据类型
2019/05/16 Python
Python进阶:生成器 懒人版本的迭代器详解
2019/06/29 Python
解决Python3 抓取微信账单信息问题
2019/07/19 Python
django-filter和普通查询的例子
2019/08/12 Python
Python内置类型性能分析过程实例
2020/01/29 Python
python判断字符串以什么结尾的实例方法
2020/09/18 Python
Python爬虫新手入门之初学lxml库
2020/12/20 Python
Python基于argparse与ConfigParser库进行入参解析与ini parser
2021/02/02 Python
白色公司:The White Company
2017/10/11 全球购物
伦敦著名的运动鞋综合商店:Footpatrol
2019/03/25 全球购物
HTML+CSS+JS实现图片的瀑布流布局的示例代码
2021/04/22 HTML / CSS
解决Jenkins集成SonarQube遇到的报错问题
2021/07/15 Java/Android