博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
根据调试工具看Vue源码之组件通信(一)
阅读量:6133 次
发布时间:2019-06-21

本文共 3970 字,大约阅读时间需要 13 分钟。

根据调试工具看Vue源码之组件通信(一)## 根据调试工具看Vue源码之组件通信(一)

在平时的业务开发中,相信在座的各位没少用过组件通信。然而,对于一些新手/业务熟手来说,不懂技术原理往往知其然而不知其所以然,用得一脸懵逼。看完本文可以帮助你了解
Vue组件的通信方式及原理,从而进一步加深对
Vue的理解,远离
CV工程师的行列。

Vue常用的组件通信方式

  • 通过$emit在子组件传参给父组件,同时触发对应的父组件函数,以此达到父子组件通信的目的
  • 通过eventbus$emit$on方法传递数据,以此实现父子组件/兄弟组件之间的通信
  • 通过Vuex将页面数据划分模块,更好更方便的管理数据

父子组件通信原理

?示例代码:

// 父组件
// 子组件

我们可以看到,父子组件的test方法中各打了一个debugger

运行程序,进入第一个断点

Vue.prototype.$emit = function (event) {  var vm = this;  ...  var cbs = vm._events[event];  if (cbs) {    cbs = cbs.length > 1 ? toArray(cbs) : cbs;    var args = toArray(arguments, 1);    var info = "event handler for \"" + event + "\"";    for (var i = 0, l = cbs.length; i < l; i++) {      invokeWithErrorHandling(cbs[i], vm, args, vm, info);    }  }  return vm};

看完上面的代码我们知道,vm._events[event]拿到了一个方法,然后调用invokeWithErrorHandling。当然,vm._events[event]的方法应该是从template上拿到的,接下来我们可以带着这几个疑问继续往下看:

  • vm._events是什么时候赋值的?
  • invokeWithErrorHandling方法是怎么执行的?

vm._events是什么时候赋值的?

在子组件的test方法中打下一个断点,选中调用堆栈中的最后一个以后可以看到add$1函数,在这里再下一个断点,重新刷新页面以后断点停在了add$1这个函数上,同时调用堆栈列表刷新,大概有这些:

  • add$1
  • updateListeners
  • updateDomListeners
  • invokeCreateHooks
  • createElm
  • ...

试探性的点进updateListeners以后,我们看到:

function updateListeners (  on,  oldOn,  add,  remove$$1,  createOnceHandler,  vm) {  var name, def$$1, cur, old, event;  // 看到这里初步猜测会遍历所有的方法  // 在chrome的断点下可以看到一个click属性,这里不知道为什么没有test方法  for (name in on) {    def$$1 = cur = on[name];    old = oldOn[name];    event = normalizeEvent(name);    // 判断当前的方法的调用器(invoker)是否是undefined,在开发环境下则会有报错提示    if (isUndef(cur)) {      process.env.NODE_ENV !== 'production' && warn(        "Invalid handler for event \"" + (event.name) + "\": got " + String(cur),        vm      );    } else if (isUndef(old)) { // 判断之前是否已存在      if (isUndef(cur.fns)) { // 判断实际上调用的函数是否是undefined        cur = on[name] = createFnInvoker(cur, vm);      }      if (isTrue(event.once)) { // 可能是挂载在一次性节点上,这里也做出判断        cur = on[name] = createOnceHandler(event.name, cur, event.capture);      }      // 断点没打在这里之前,event.name一直是“click”      add(event.name, cur, event.capture, event.passive, event.params);    } else if (cur !== old) {      old.fns = cur;      on[name] = old;    }  }  for (name in oldOn) {    if (isUndef(on[name])) {      event = normalizeEvent(name);      remove$$1(event.name, oldOn[name], event.capture);    }  }}

整理完上面这个函数的逻辑以后,将断点打在add上,刷新页面后断点停在这里,步进这个函数:

function add (event, fn) {  target.$on(event, fn);}

显然target是全局变量,但是这里先不深究。再次步进之后可以看到断点停在这里:

Vue.prototype.$on = function (event, fn) {    var vm = this;    if (Array.isArray(event)) {      for (var i = 0, l = event.length; i < l; i++) {        vm.$on(event[i], fn);      }    } else {      (vm._events[event] || (vm._events[event] = [])).push(fn);      // optimize hook:event cost by using a boolean flag marked at registration      // instead of a hash lookup      if (hookRE.test(event)) {        vm._hasHookEvent = true;      }    }    return vm  };

可见父子组件通信过程中,尽管$on对开发者不可见,但是最终还是要走$on函数,这里感觉跟使用eventbus大同小异。

至此,刚才提出的第一个疑问已经解决:)

invokeWithErrorHandling方法是怎么执行的?

在一开始的基础上,直接步进invokeWithErrorHandling方法:

function invokeWithErrorHandling (  handler,  context,  args,  vm,  info) {  var res;  try {    // 判断是否有参数,然后分情况调用    res = args ? handler.apply(context, args) : handler.call(context);    // 处理异步函数的情况    if (res && !res._isVue && isPromise(res)) {      // issue #9511      // reassign to res to avoid catch triggering multiple times when nested calls      res = res.catch(function (e) { return handleError(e, vm, info + " (Promise/async)"); });    }  } catch (e) {    handleError(e, vm, info);  }  return res}

最后重新梳理下父子组件通信的实现逻辑:

  • 赋值vm._events[event]
  1. 页面初始化时,Vue调用updateListeners函数(当然,在那之前会生成虚拟dom,也就是vnode

这里暂不深究),在函数里面调用createFnInvoker方法,给模板上的方法再套一层调用器(invoker)

  1. 调用target.$on方法
  2. 递归处理event为数组的情况
  3. vm._events[event]赋值
  • invokeWithErrorHandling方法是怎么执行的?
  1. 判断是否有参数,然后分情况调用
  2. 处理异步函数的情况

⚠️注意:由于Vue会在方法上再封装一层调用器(invoker),所以在在调用堆栈这里往往会出现两个invokeWithErrorHandling方法

转载地址:http://hoeua.baihongyu.com/

你可能感兴趣的文章
《Cisco IPv6网络实现技术(修订版)》一2.7 复习题
查看>>
Facebook 开源 Android 调试工具 —— Stetho
查看>>
生活不止有苟且,还有N个免费DevOps开源工具
查看>>
视频直播Android推流SDK初体验
查看>>
第十三天:制定预算
查看>>
java技术团队必须要注意的那几个点
查看>>
Hibernate ORM 5.1.7 发布,数据持久层框架
查看>>
数百万网站因流行 PHP 脚本的安全漏洞而受影响
查看>>
《走进SAP(第2版)》——2.7 SAP对业务流程的支持
查看>>
《C语言解惑》—— 2.9 输出值的操作符
查看>>
Project Volta 让 Android 续航提升了多少?
查看>>
《树莓派实战秘籍》——1.7 技巧07使用过压获得更高的性能
查看>>
《SAS 统计分析与应用从入门到精通(第二版)》一1.4 SAS系统的文件管理
查看>>
《众妙之门——网页设计专业之道》——2.4 总结
查看>>
MySQL sql_mode 说明(及处理一起 sql_mode 引发的问题)
查看>>
Java 注解详解 (annotation)
查看>>
鹰眼跟踪、限流降级,EDAS的微服务解决之道
查看>>
秘籍:程序猿该如何实力撩妹
查看>>
网络编程socket基本API详解
查看>>
API接口设计 OAuth2.0认证
查看>>