源码分析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 动态生成私有变量访问器
Dec 06 Javascript
js随机颜色代码的多种实现方式
Apr 23 Javascript
JavaScript实现仿新浪微博大厅和腾讯微博首页滚动特效源码
Sep 15 Javascript
使用Javascript实现选择下拉菜单互移并排序
Feb 23 Javascript
AngularJS基础 ng-keypress 指令简单示例
Aug 02 Javascript
Vue.js实现简单动态数据处理
Feb 13 Javascript
Angular之指令Directive用法详解
Mar 01 Javascript
webstorm添加vue.js支持的方法教程
Jul 05 Javascript
JS判断数组那点事
Oct 10 Javascript
详解基于Koa2开发微信二维码扫码支付相关流程
May 16 Javascript
Node.js API详解之 dns模块用法实例分析
May 15 Javascript
vue项目如何打包之项目打包优化(让打包的js文件变小)
Apr 30 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
php下实现折线图效果的代码
2007/04/28 PHP
php checkbox 取值详细说明
2010/08/19 PHP
PHP实现数字补零功能的2个函数介绍
2014/05/12 PHP
ThinkPHP中I(),U(),$this-&gt;post()等函数用法
2014/11/22 PHP
php计算两个日期相差天数的方法
2015/03/14 PHP
浅析php-fpm静态和动态执行方式的比较
2016/11/09 PHP
Referer原理与图片防盗链实现方法详解
2019/07/03 PHP
通过Javascript创建一个选择文件的对话框代码
2012/06/16 Javascript
浏览器加载、渲染和解析过程黑箱简析
2012/11/29 Javascript
Jquery获取和修改img的src值的方法
2014/02/17 Javascript
JavaScript利用正则表达式去除日期中的“-”
2014/07/01 Javascript
阿里云ecs服务器中安装部署node.js的步骤
2016/10/08 Javascript
EditPlus中的正则表达式 实战(4)
2016/12/15 Javascript
原生node.js案例--前后台交互
2017/02/20 Javascript
jQuery插件echarts实现的多柱子柱状图效果示例【附demo源码下载】
2017/03/04 Javascript
微信小程序中上传图片并进行压缩的实现代码
2018/08/28 Javascript
Angular刷新当前页面的实现方法
2018/11/21 Javascript
trackingjs+websocket+百度人脸识别API实现人脸签到
2018/11/26 Javascript
JS使用new操作符创建对象的方法分析
2019/05/30 Javascript
Vue中keep-alive 实现后退不刷新并保持滚动位置
2020/03/17 Javascript
python tensorflow基于cnn实现手写数字识别
2018/01/01 Python
python list数据等间隔抽取并新建list存储的例子
2019/11/27 Python
python GUI库图形界面开发之PyQt5简单绘图板实例与代码分析
2020/03/08 Python
Python连接Hadoop数据中遇到的各种坑(汇总)
2020/04/14 Python
通过Python pyecharts输出保存图片代码实例
2020/11/25 Python
html5使用Drag事件编辑器拖拽上传图片的示例代码
2017/08/22 HTML / CSS
HTML文本属性&amp;颜色控制属性的实现
2019/12/17 HTML / CSS
教师评优的个人自我评价分享
2013/09/19 职场文书
挂职思想汇报
2013/12/31 职场文书
司马光教学反思
2014/02/01 职场文书
办公室文员岗位职责范本
2014/06/12 职场文书
银行转正自我鉴定
2014/09/29 职场文书
学生逃课检讨书1000字
2014/10/20 职场文书
手机销售员岗位职责
2015/04/11 职场文书
JavaScript 防篡改对象的用法示例
2021/04/24 Javascript
Python并发编程实例教程之线程的玩法
2021/06/20 Python