源码分析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 随机数产生6位数字
May 13 Javascript
JavaScript类属性的访问方式详解
Feb 11 Javascript
使用js操作css实现js改变背景图片示例
Mar 10 Javascript
IE下双击checkbox反应延迟问题的解决方法
Mar 27 Javascript
javascript动画算法实例分析
Jul 31 Javascript
jquery实现漂亮的二级下拉菜单代码
Aug 26 Javascript
js仿百度登录页实现拖动窗口效果
Mar 11 Javascript
JS导出PDF插件的方法(支持中文、图片使用路径)
Jul 12 Javascript
详解Node.js串行化流程控制
May 04 Javascript
实例分析javascript中的异步
Jun 02 Javascript
AngularJs的$http发送POST请求,php无法接收Post的数据问题及解决方案
Aug 13 Javascript
原生js实现表格翻页和跳转
Sep 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
PHP学习之整理字符串
2011/04/17 PHP
实测在class的function中include的文件中非php的global全局环境
2013/07/15 PHP
利用php_imagick实现复古效果的方法
2016/10/18 PHP
jquery图片上下tab切换效果
2011/03/18 Javascript
jquery控制左右箭头滚动图片列表的实例
2013/05/20 Javascript
jQuery实现跨域
2015/02/03 Javascript
javascript模拟php函数in_array
2015/04/27 Javascript
AngularJS表达式讲解及示例代码
2016/08/16 Javascript
Angular.js中angular-ui-router的简单实践
2017/07/18 Javascript
Angular.js中数组操作的方法教程
2017/07/31 Javascript
AngularJS中的作用域实例分析
2018/05/16 Javascript
详解解决使用axios发送json后台接收不到的问题
2018/06/27 Javascript
解决Angular4项目部署到服务器上刷新404的问题
2018/08/31 Javascript
js中apply和call的理解与使用方法
2019/11/27 Javascript
jQuery实现全选、反选和不选功能的方法详解
2019/12/04 jQuery
webpack5 联邦模块介绍详解
2020/07/08 Javascript
js利用iframe实现选项卡效果
2020/08/09 Javascript
初步理解Python进程的信号通讯
2015/04/09 Python
python实现的简单抽奖系统实例
2015/05/22 Python
Python的Flask框架中使用Flask-SQLAlchemy管理数据库的教程
2016/06/14 Python
python 中的divmod数字处理函数浅析
2017/10/17 Python
浅谈python数据类型及类型转换
2017/12/18 Python
Django框架教程之正则表达式URL误区详解
2018/01/28 Python
通过Python模块filecmp 对文件比较的实现方法
2018/06/29 Python
Python绘制的二项分布概率图示例
2018/08/22 Python
Python读写文件基础知识点
2019/06/10 Python
python基于gevent实现并发下载器代码实例
2019/11/01 Python
详解centos7+django+python3+mysql+阿里云部署项目全流程
2019/11/15 Python
python标准库OS模块详解
2020/03/10 Python
浅谈Python爬虫原理与数据抓取
2020/07/21 Python
纯CSS3制作的鼠标悬停时边框旋转
2017/01/03 HTML / CSS
房地产财务部员工岗位职责
2014/03/12 职场文书
医学专业大学生求职信
2014/07/12 职场文书
还款承诺书范本
2015/01/20 职场文书
中标通知书范本
2015/04/17 职场文书
详解Python魔法方法之描述符类
2021/05/26 Python