好家伙,
0、一个例子
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <title>Vue 父子组件通信示例</title>
-
- </head>
- <body>
-
- <parent-component></parent-component>
-
-
- </body>
- </html>
复制代码
1、$emit、$on源码
源码实现,我们来看$emit、$on的源码实现部分- Vue.prototype.$on = function (event, fn) {
- var vm = this;
- if (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;
- };
- Vue.prototype.$emit = function (event) {
- var vm = this;
- // 处理大小写
- {
- var lowerCaseEvent = event.toLowerCase();
- if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
- tip("Event "".concat(lowerCaseEvent, "" is emitted in component ") +
- "".concat(formatComponentName(vm), " but the handler is registered for "").concat(event, "". ") +
- "Note that HTML attributes are case-insensitive and you cannot use " +
- "v-on to listen to camelCase events when using in-DOM templates. " +
- "You should probably use "".concat(hyphenate(event), "" instead of "").concat(event, ""."));
- }
- }
- var cbs = vm._events[event];
- if (cbs) {
- cbs = cbs.length > 1 ? toArray(cbs) : cbs;
- var args = toArray(arguments, 1);
- var info = "event handler for "".concat(event, """);
- for (var i = 0, l = cbs.length; i < l; i++) {
- invokeWithErrorHandling(cbs[i], vm, args, vm, info);
- }
- }
- return vm;
- };
- 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) && !res._handled) {
- res.catch(function (e) { return handleError(e, vm, info + " (Promise/async)"); });
- res._handled = true;
- }
- }
- catch (e) {
- handleError(e, vm, info);
- }
- return res;
- }
复制代码
2.代码解释
看着比较复杂,所以我们精简一下,去掉性能优化和一些正则表达式还有一些数组处理
精简下来无非几句代码
$on- (vm._events[event] || (vm._events[event] = [])).push(fn);
复制代码 $emit- var cbs = vm._events[event];
- invokeWithErrorHandling(cbs[i], vm, args, vm, info);
- function invokeWithErrorHandling(handler, context, args, vm, info) {
-
- res = args ? handler.apply(context, args) : handler.call(context);
- return res;
- }
复制代码
分析:
$emit、$on的实现使用了观察者模式的设计思想
$on方法用于在当前Vue实例上注册事件监听器。
vm._events:维护一个事件与其处理函数的映射。每个事件对应一个数组,数组中存放了所有注册的处理函数。
$emit方法用于触发事件,当事件被触发时,调用所有注册在该事件上的处理函数。
非常简单
3.源码注释版本
- // 在Vue的原型上定义一个方法$on
- Vue.prototype.$on = function (event, fn) {
- // vm指的是Vue的实例
- var vm = this;
- // 如果event是一个数组,那么对每个事件递归调用$on方法
- if (isArray(event)) {
- for (var i = 0, l = event.length; i < l; i++) {
- vm.$on(event[i], fn);
- }
- }
- // 如果event不是一个数组,那么将函数fn添加到vm._events[event]中
- else {
- (vm._events[event] || (vm._events[event] = [])).push(fn);
- // 如果event是一个钩子事件,那么设置vm._hasHookEvent为true
- if (hookRE.test(event)) {
- vm._hasHookEvent = true;
- }
- }
- // 返回Vue的实例
- return vm;
- };
- // 在Vue的原型上定义一个方法$emit
- Vue.prototype.$emit = function (event) {
- // vm指的是Vue的实例
- var vm = this;
- // 处理事件名的大小写
- {
- var lowerCaseEvent = event.toLowerCase();
- // 如果事件名的小写形式和原事件名不同,并且vm._events中有注册过小写的事件名
- if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
- // 那么提示用户事件名的大小写问题
- tip("Event "".concat(lowerCaseEvent, "" is emitted in component ") +
- "".concat(formatComponentName(vm), " but the handler is registered for "").concat(event, "". ") +
- "Note that HTML attributes are case-insensitive and you cannot use " +
- "v-on to listen to camelCase events when using in-DOM templates. " +
- "You should probably use "".concat(hyphenate(event), "" instead of "").concat(event, ""."));
- }
- }
- // 获取vm._events[event]中的所有回调函数
- var cbs = vm._events[event];
- // 如果存在回调函数
- if (cbs) {
- // 如果回调函数的数量大于1,那么将其转换为数组
- cbs = cbs.length > 1 ? toArray(cbs) : cbs;
- // 获取除event外的其他参数
- var args = toArray(arguments, 1);
- // 定义错误处理信息
- var info = "event handler for "".concat(event, """);
- // 对每个回调函数进行错误处理
- for (var i = 0, l = cbs.length; i < l; i++) {
- invokeWithErrorHandling(cbs[i], vm, args, vm, info);
- }
- }
- // 返回Vue的实例
- return vm;
- };
- // 定义一个错误处理函数
- function invokeWithErrorHandling(handler, context, args, vm, info) {
- var res;
- try {
- // 如果存在参数args,那么使用apply方法调用handler,否则使用call方法调用handler
- res = args ? handler.apply(context, args) : handler.call(context);
- // 如果返回结果res存在,且res不是Vue实例,且res是一个Promise,且res没有被处理过
- if (res && !res._isVue && isPromise(res) && !res._handled) {
- // 那么对res进行错误处理,并标记res已经被处理过
- res.catch(function (e) { return handleError(e, vm, info + " (Promise/async)"); });
- res._handled = true;
- }
- }
- // 如果在执行过程中抛出错误,那么进行错误处理
- catch (e) {
- handleError(e, vm, info);
- }
- // 返回结果res
- return res;
- }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |