根据调试工具看Vue源码之组件通信(一)
栏目: JavaScript · 发布时间: 6年前
内容简介:在平时的业务开发中,相信在座的各位没少用过组件通信。然而,对于一些新手/业务熟手来说,不懂技术原理往往知其然而不知其所以然,用得一脸懵逼。看完本文可以帮助你了解我们可以看到,父子组件的看完上面的代码我们知道,
根据调试 工具 看Vue源码之组件通信(一)## 根据调试工具看Vue源码之组件通信(一)
在平时的业务开发中,相信在座的各位没少用过组件通信。然而,对于一些新手/业务熟手来说,不懂技术原理往往知其然而不知其所以然,用得一脸懵逼。看完本文可以帮助你了解 Vue
组件的通信方式及原理,从而进一步加深对 Vue
的理解,远离 CV
工程师的行列。
Vue
常用的组件通信方式
-
通过
$emit在子组件传参给父组件,同时触发对应的父组件函数,以此达到父子组件通信的目的 -
通过
eventbus的$emit和$on方法传递数据,以此实现父子组件/兄弟组件之间的通信 -
通过
Vuex将页面数据划分模块,更好更方便的管理数据
父子组件通信原理
:chestnut:示例代码:
// 父组件
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld
msg="Welcome to Your Vue.js App"
@test="test"/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'app',
methods: {
test (param) {
debugger
console.log('param-->', param);
}
},
components: {
HelloWorld
}
}
</script>
// 子组件
<template>
<div class="wrapper">
<button @click="test">按钮</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
methods: {
test () {
debugger
this.$emit('test', '666')
}
}
}
</script>
我们可以看到,父子组件的 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]
-
页面初始化时,
Vue调用updateListeners函数(当然,在那之前会生成虚拟dom,也就是vnode,
这里暂不深究),在函数里面调用 createFnInvoker
方法,给模板上的方法再套一层调用器(invoker)
target.$on event vm._events[event]
-
invokeWithErrorHandling方法是怎么执行的?
- 判断是否有参数,然后分情况调用
- 处理异步函数的情况
:warning:注意:由于 Vue
会在方法上再封装一层调用器( invoker
),所以在在调用堆栈这里往往会出现两个 invokeWithErrorHandling
方法
以上所述就是小编给大家介绍的《根据调试工具看Vue源码之组件通信(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- LLDebugTool - 便捷的IOS调试工具(支持组件化)
- iOS常用调试方法:断点调试
- 断点调试和日志调试之间的平衡点:函数计算调试之 Python 篇
- .NET高级调试系列-Windbg调试入门篇
- VisualStudio 通过外部调试方法快速调试库代码
- GDB 调试 Mysql 实战(二)GDB 调试打印
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web Design Index 7
Pepin Press / PEPIN PRESS / 20070501 / TWD$1000.00
《網頁設計索引》年刊自2000年誕生起現已發展成同行業最重要的出版物之一,每年都會對網頁設計的最新趨勢給予準確概述。網站可簡單到只有一頁,也可以設計為具有最新數位性能的複雜結構。《網頁設計索引》的篩選標準是根據設計品質、創意及效率-而不管複雜程度如何。因此在本書中你可以找到所有可能的樣式和風格的實例。 每輯《網頁設計索引》都展示了1002個精采的網頁 同時提供了每個網頁的URL。網頁設計和編......一起来看看 《Web Design Index 7》 这本书的介绍吧!
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
RGB HSV 转换
RGB HSV 互转工具