源码分析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 相关文章推荐
javascript 尚未实现错误解决办法
Nov 27 Javascript
jsvascript图像处理—(计算机视觉应用)图像金字塔
Jan 15 Javascript
js浮点数精确计算(加、减、乘、除)
Dec 26 Javascript
jquery文档操作wrap()方法实例简述
Jan 10 Javascript
JavaScript中constructor()方法的使用简介
Jun 05 Javascript
JS实现简单的图书馆享元模式实例
Jun 30 Javascript
vue父组件向子组件传递多个数据的实例
Mar 01 Javascript
用p5.js制作烟花特效的示例代码
Mar 21 Javascript
VUE 3D轮播图封装实现方法
Jul 03 Javascript
vue实现中部导航栏布局功能
Jul 30 Javascript
jQuery实现滑动星星评分效果(每日分享)
Nov 13 jQuery
微信小程序自定义yPicker组件实现省市区三级联动功能
Oct 29 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
PHP4实际应用经验篇(3)
2006/10/09 PHP
php生成随机数的三种方法
2014/09/10 PHP
验证坐标在某坐标区域内php代码
2016/10/08 PHP
PHP封装类似thinkphp连贯操作数据库Db类与简单应用示例
2019/05/08 PHP
js获取单选按钮的数据
2006/11/27 Javascript
javascript下给元素添加事件的方法与代码
2007/08/13 Javascript
JS下拉框内容左右移动效果的具体实现
2013/07/10 Javascript
JavaScript中遍历对象的property的3种方法介绍
2014/12/30 Javascript
jquery.form.js实现将form提交转为ajax方式提交的方法
2015/04/07 Javascript
JS实现仿新浪黄色经典滑动门效果代码
2015/09/27 Javascript
Javascript HTML5 Canvas实现的一个画板
2020/04/12 Javascript
jQuery绑定事件-多种实现方式总结
2016/05/09 Javascript
详解AngularJs中$resource和restfu服务端数据交互
2016/09/21 Javascript
jquery html5 视频播放控制代码
2016/11/06 Javascript
jQuery中table数据的值拷贝和拆分
2017/03/19 Javascript
jQuery validata插件实现方法
2017/06/25 jQuery
浅谈ES6 模板字符串的具体使用方法
2017/11/07 Javascript
微信小程序使用form表单获取输入框数据的实例代码
2018/05/17 Javascript
深入理解Vue 组件之间传值
2018/08/16 Javascript
微信小程序开发常见问题及解决方案
2019/07/11 Javascript
Vue使用自定义指令实现拖拽行为实例分析
2020/06/06 Javascript
Vue router传递参数并解决刷新页面参数丢失问题
2020/12/02 Vue.js
由浅入深讲解python中的yield与generator
2017/04/05 Python
Django自定义列表 models字段显示方式
2020/04/03 Python
python 19个值得学习的编程技巧
2020/08/15 Python
python 制作网站小说下载器
2021/02/20 Python
英国手机零售商:Metrofone
2019/03/18 全球购物
Linux开机引导的步骤是什么
2014/02/26 面试题
简历的自我评价范文
2014/02/04 职场文书
毕业典礼演讲稿
2014/05/13 职场文书
医德医风演讲稿
2014/05/20 职场文书
地震捐款倡议书
2014/08/29 职场文书
公务员培的训心得体会
2014/09/01 职场文书
邓小平理论心得体会
2014/09/09 职场文书
Nginx 负载均衡是什么以及该如何配置
2021/03/31 Servers
PHP RabbitMQ消息列队
2022/05/11 PHP