彻底搞清楚vue3的defineExpose宏函数是如何暴露方法给父组件使用
前言
众所周知,当子组件使用setup后,父组件就不能像vue2那样直接就可以访问子组件内的属性和方法。这个时候就需要在子组件内使用defineExpose
宏函数来指定想要暴露出去的属性和方法。这篇文章来讲讲defineExpose
宏函数是如何暴露出去这些属性和方法给父组件使用。注:本文中使用的vue版本为3.4.19
。
看个demo
父组件index.vue
的代码如下:
<template>
<ChildDemo ref="child" />
<button @click="handleClick">调用子组件的validate方法</button>
</template>
<script setup lang="ts">
import ChildDemo from "./child.vue";
import { ref } from "vue";
const child = ref();
function handleClick() {
console.log(child.value.validate);
child.value.validate?.();
}
</script>
上面的代码很简单,通过ref
拿到子组件的实例赋值给child
变量。然后在按钮的click事件中打印出子组件的validate
方法和执行validate
方法。
再来看看子组件child.vue
不使用defineExpose
宏的例子,代码如下:
<template></template>
<script setup>
function validate() {
console.log("执行子组件validate方法");
}
</script>
在浏览器中点击父组件的button按钮,可以看到控制台中打印的是undefined
,并且子组件内的validate
方法也没有执行。因为子组件使用了setup,默认是不会暴露setup中定义的属性和方法。如下图:
我们再来看看子组件child.vue
使用defineExpose
宏的例子,代码如下:
<template></template>
<script setup>
function validate() {
console.log("执行子组件validate方法");
}
defineExpose({
validate,
});
</script>
在浏览器中点击父组件的button按钮,可以看到控制台中打印的不再是undefined
,子组件内的validate
方法也执行了。如下图:
关注公众号:【前端欧阳】,给自己一个进阶vue的机会
加我微信heavenyjj0012回复「666」,免费领取欧阳研究vue源码过程中收集的源码资料,欧阳写文章有时也会参考这些资料。同时让你的朋友圈多一位对vue有深入理解的人。
编译后的代码
首先需要在浏览器中找到编译后的使用defineExpose
宏的child.vue
文件,在network面板中找到child.vue
,然后右键点击Open in Sources panel就可以在source面板中找到编译后的child.vue
。如下图:
为了要在浏览器中debug,我们还需要在设置中关闭浏览器的javascript source map,如下图:
现在我们来看看编译后的child.vue
文件,代码如下:
const _sfc_main = {
__name: "child",
setup(__props, { expose: __expose }) {
function validate() {
console.log("执行子组件validate方法");
}
__expose({
validate,
});
const __returned__ = { validate };
return __returned__;
},
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return null;
}
_sfc_main.render = _sfc_render;
export default _sfc_main;
从上面可以看到_sfc_main
对象中的setup
对应的就是我们源代码<script setup>
中的内容,并且defineExpose
宏函数也不在了,变成了一个__expose
方法(defineExpose
宏函数如何编译成__expose
方法我们会在下一篇文章讲)。如下图:
expose
方法
给__expose
方法打个断点,刷新页面此时断点停留在__expose
方法上面。点击step into
进入到__expose
方法内部,如下图:
进入到__expose
方法内部,我们发现__expose
方法是在一个createSetupContext
函数中定义的。在我们这个场景中createSetupContext
函数简化后的代码如下:
function createSetupContext(instance) {
const expose = (exposed) => {
instance.exposed = exposed || {};
};
return Object.freeze({
// ...省略
expose,
});
}
我们先来看看函数中的instance
变量,我想你通过名字应该已经猜到了他就是当前vue实例对象。如下图:
在vue实例对象中有我们熟悉的data方法、directives和componens属性等。
在expose
函数内部做的事情也很简单,将子组件需要暴露的属性或者方法组成的对象赋值给vue实例上的exposed
属性。
父组件访问子组件的validate
方法
在vue3中想要访问子组件需要使用特殊的 ref
attribute,在我们这个例子中就是使用<ChildDemo ref="child" />
。这样使用后就可以使用child
变量访问子组件,其实在这里child
变量的值就是一个名为getExposeProxy
函数的返回值(后面的文章中会去详细讲解ref
attribute是如何访问子组件)。
getExposeProxy
函数的代码如下:
function getExposeProxy(instance) {
if (instance.exposed) {
return (
instance.exposeProxy ||
(instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), {
get(target, key) {
if (key in target) {
return target[key];
} else if (key in publicPropertiesMap) {
return publicPropertiesMap[key](instance);
}
},
has(target, key) {
// ...省略
},
}))
);
}
}
前面我们讲过了defineExpose
宏函数中定义了想要暴露出来的属性和方法,经过编译后defineExpose
宏函数变成了__expose
方法。执行__expose
方法后会将子组件想要暴露的属性或者方法组成的对象赋值给vue实例上的exposed
属性,也就是instance.exposed
。
在上面的getExposeProxy
函数中就是返回了instance.exposed
的Proxy
对象,当我们使用child.value.validate
访问子组件的validate
方法,其实就是访问的是instance.exposed
对象中的validate
方法,而instance.exposed
中的validate
方法就是defineExpose
宏函数暴露的validate
方法。如下图:
总结
父组件想要访问子组件暴露的validate
方法主要分为下面四步:
子组件使用
defineExpose
宏函数声明想要暴露validate
方法。defineExpose
宏函数经过编译后变成__expose
方法。执行
__expose
方法将子组件需要暴露的属性或者方法组成的对象赋值给子组件vue实例上的exposed
属性,也就是instance.exposed
。父组件使用ref访问子组件的
validate
方法,也就是访问child.value.validate
。其实访问的就是上一步的instance.exposed.validate
方法,最终访问的就是defineExpose
宏函数中暴露的validate
方法。
关注(图1)公众号:【前端欧阳】,解锁我更多vue原理文章。
加我(图2)微信回复「666」,免费领取欧阳研究vue源码过程中收集的源码资料,欧阳写文章有时也会参考这些资料。同时让你的朋友圈多一位对vue有深入理解的人。
彻底搞清楚vue3的defineExpose宏函数是如何暴露方法给父组件使用的更多相关文章
- FrameWork模型中引入宏函数报错解决方法
如下图在Framework的一个简单维度中加入宏函数 解决办法如下图 step1: step2: PS :Cognos 10.1.1中 在cognos connection中创建数据源,为什么没有od ...
- 【转】linux内核中writesb(), writesw(), writesl() 宏函数
writesb(), writesw(), writesl() 宏函数 功能 : writesb() I/O 上写入 8 位数据流数据 (1字节) writesw() I/O 上写入 16 ...
- 来一轮带注释的demo,彻底搞懂javascript中的replace函数
javascript这门语言一直就像一位带着面纱的美女,总是看不清,摸不透,一直专注服务器端,也从来没有特别重视过,直到最近几年,javascript越来越重要,越来越通用.最近和前端走的比较近,借此 ...
- readb(), readw(), readl(),writeb(), writew(), writel() 宏函数【转】
转自:http://www.netfoucs.com/article/hustyangju/70429.html readb(), readw(), readl()函数功能:从内存映射的 I/O 空间 ...
- FrameWork中SQLServer数据源使用宏函数出错解决办法
环境:DW是SQLServer2008 建模工具:Cognos FrameWork Version:Cognos10.2 出现问题:在FM中创建查询主题,sql类型数据源,引入宏函数#CAMIDLis ...
- Report Studio值提示通过prompt宏函数给sql查询传参
场景:当我们在DW中新建了一个表,但是在FM中没有创建模型,想针对这个表直接做一个报表,那么就需要在reportstudio中直接用sql来查询,为了追求查询速度,我们可以把页面用户选择的条件直接传给 ...
- SAS学习笔记44 宏函数
类SAS函数的宏函数 该部分函数共5个,其无论是名字.语法.功能都与SAS函数类似,只是在函数名前多了一个“%”.这5个宏函数分别是: %INDEX %LENGTH %SCAN %SUBSTR %UP ...
- #define定义宏函数 的正确使用
如何使用宏来定义一个自定义函数呢?首先我们来看下面这段代码 #define SQUARE(x) x*x int main() { int a = 5; printf("SQUARE(a): ...
- 终于搞懂了vue 的 render 函数(一) -_-|||
终于搞懂了vue 的 render 函数(一) -_-|||:https://blog.csdn.net/sansan_7957/article/details/83014838 render: h ...
- C++中的间接宏函数
宏函数对于每个C++程序员都决不陌生,就算是初出茅庐的C++程序员也知道如何定义.使用宏函数. 但是当初学者看到类似于以下这种宏函数嵌套的时候,可能还是会比较嘀咕, #define CONVERT ...
随机推荐
- 【直播合集】HDC.Together 2023 精彩回顾!收藏勿错过~
HDC.Together 2023 主题演讲 万象复兴,热潮澎湃,HarmonyOS 全面进化,迈入新纪元.以创新改变世界,以生态驱动未来.扬帆起航,就在此刻.新版本.新体验.新流量.新商业.新机遇. ...
- android 找不到设备
前言 当我们安装android studio的时候,测试的时候,你可能找不到设备. 我遇到的有两种情况,一种是本身就需要安装插件,如一些低端机或者有些小米机. 还有一种情况需要去触发一下,有些华为手机 ...
- 深度解读《深度探索C++对象模型》之默认构造函数
接下来我将持续更新"深度解读<深度探索C++对象模型>"系列,敬请期待,欢迎关注!也可以关注公众号:iShare爱分享,主动获得推文. 提到默认构造函数,很多文章和书籍 ...
- MAC上Cisco AnyConnect删除不干净,造成无法重新安装的解决办法
1.问题 由于直接删除而不是正常卸载,导致文件残留,无法正常安装,并且软件不可用 2.解决 2.1 终端运行命令 pkgutil --pkgs|grep com.cisco 查看cisco残留的文件 ...
- 通过 MSE 实现基于Apache APISIX的全链路灰度
简介: 无论是微服务网关还是微服务本身都需要识别流量,根据治理规则做出动态决策.当服务版本发生变化时,这个调用链路的转发也会实时改变.相比于利用机器搭建的灰度环境,这种方案不仅可以节省大量的机器成本和 ...
- Java依赖冲突高效解决之道
简介:由于阿里妈妈联盟团队负责业务的特殊性,系统有庞大的对外依赖,依赖集团六七十个团队服务及N多工具组件,通过此文和大家分享一下我们积累的一些复杂依赖有效治理的经验,除了简单技术技巧的总结外,也会探 ...
- 让容器跑得更快:CPU Burst 技术实践
简介:让人讨厌的 CPU 限流影响容器运行,有时人们不得不牺牲容器部署密度来避免 CPU 限流出现.我们设计的 CPU Burst 技术既能保证容器运行服务质量,又不降低容器部署密度.CPU Bur ...
- GRPC: 如何优雅关闭进程(graceful shutdown)
简介: 本文将介绍优雅关闭 gRPC 微服务.在进程收到关闭信号时,我们需要关闭后台运行的逻辑,比如,MySQL 连接等等. 介绍 本文将介绍优雅关闭 gRPC 微服务. 什么是优雅关闭? 在进程收 ...
- EMR on ACK 全新发布,助力企业高效构建大数据平台
简介: 阿里云 EMR on ACK 为用户提供了全新的构建大数据平台的方式,用户可以将开源大数据服务部署在阿里云容器服务(ACK)上.利用 ACK 在服务部署和对高性能可伸缩的容器应用管理的能力优 ...
- dotnet 8 破坏性改动 在 AssemblyInformationalVersionAttribute 添加上 git 的 commit 号
我在一个 WPF 项目里面,在界面显示应用的版本号,更新到 dotnet 8 的 SDK 之后,发现我的界面布局损坏了.本质上这个破坏性改动和 WPF 没有什么关系,是 dotnet 的 SDK 或编 ...