Vue3+TS项目 - ref和useTemplateRef获取组件实例

news/2024/10/8 8:10:52/

        在Vue2中,子组件使用的是选项式 API ,被引用的组件实例和该子组件的 this 完全一致,这意味着父组件对子组件的每一个属性和方法都有完全的访问权。这使得在父组件和子组件之间创建紧密耦合的实现细节变得很容易,当然也因此,应该只在绝对需要时才使用组件引用。大多数情况下,你应该首先使用标准的 props 和 emit 接口来实现父子组件交互。

        而在Vue3中,子组件使用<script setup>的组件是默认私有的:一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露。

一、创建子组件(表格)

        在项目目录src/components目录中创建TableNormal.vue组件,并且通过defineExpose对外暴露父组件可以调用的函数。代码如下:

<template><div class="table-wrap"><!-- table-wrap --><div class="table-wrap"><el-table ref="table" border :data="tableData"><el-table-column type="selection" width="55" fixed></el-table-column><el-table-column type="index" label="序号" width="50px" :resizable="false"></el-table-column><template v-for="(item, index) in tableColumns"><el-table-column :prop="item.prop" :label="item.label" :key="'' + index"></el-table-column></template.</el-table></div><!-- /table-wrap --></div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
defineOptions({name: "Table-Normal"
})
type TableColumnsType = {label: string,prop: string
}
const tableColumns = ref<Array<TableColumnsType>>([])       // 列数据
const tableData = ref<Array<any>>([])                       // 行数据
const page = ref(1)
const pageSize = ref(5)
const pageTotal = ref(0)const onLoadTableColumns = () => {// 通过Axios API接口获取tableColumns 表格列数据
}const onLoadTableData = () => {// 通过Axios API接口获取tableData 表格行数据
}onMounted(() => {onLoadTableColumns()onLoadTableData()
})// 对外暴露onLoadTableData函数
defineExpose({onLoadTableData})
</script>

二、创建父组件(Department)

        在项目src/views目录中,创建Department.vue文件。示例代码如下:

<template><div><table-normal></table-normal><button @click="() => valueChange()">重新加载</button></div>
</template>
<script lang="ts" setup>
// 重新加载
const valueChange = () => {}
</script>

2.1 ref获取组件实例

        通过ref获取组件实例后,则可以通过实例对象的变量,去调用子组件中对外暴露的变量或函数了。

<template><div><table-normal ref="tableNormalRef"></table-normal><button @click="() => valueChange()">重新加载</button></div>
</template>
<script lang="ts" setup>
import { ref} from "vue";
const tableNormalRef = ref(null) // 重新加载
const valueChange = () => {if(tableNormalRef.value && tableNormalRef.value.onLoadTableData) tableNormalRef.value.onLoadTableData()
}
</script>

三、解决问题 - 类型断言

        当父组件Department.vue中,通过ref获取自定义表格组件实例,并且在子组件中已通过defineExpose对外暴露了onLoadTableData函数,所以我们可以通过实例变量直接调用onLoadTableData()函数。

if(tableNormalRef.value && tableNormalRef.value.onLoadTableData) tableNormalRef.value.onLoadTableData()

3.1 "never"断言

        但是调用后,会提示如下截图上的断言提示【错误提示:类型“never”上不存在属性“onLoadTableData”。】。

        在TypeScript中,当一个变量被推断为never类型时,这意味着变量从逻辑上不应该存在任何值。所以需要在获取实例时,告诉ref该实例的类型。

        由于ref获取自定义组件为DOM元素,所以将其类型指定为HTMLElement,代码如下:

<template><div><table-normal ref="tableNormalRef"></table-normal><button @click="() => valueChange()">重新加载</button></div>
</template>
<script lang="ts" setup>
import { ref} from "vue";
const tableNormalRef = ref<HTMLElement | null>(null)// 重新加载
const valueChange = () => {if(tableNormalRef.value && tableNormalRef.value.onLoadTableData) tableNormalRef.value.onLoadTableData()
}
</script>

3.2 onLoadTableData不存在

        添加HTMLElement后,never类型断言问题是消失了,但随之而来又有新问题【错误:属性“onLoadTableData”在类型“HTMLElement”上不存在。你是否指的是“onloadeddata”?

        这是由于onLoadTableData为自定义函数,在HTMLElement类型中并不存上在,所以我们需要重新定义一个新接口类型,并继承HTMLElement类型即可。代码如下:

<template><div><table-normal ref="tableNormalRef"></table-normal><button @click="() => valueChange()">重新加载</button></div>
</template>
<script lang="ts" setup>
import { ref} from "vue";
// 定义新接口类型
interface LoadTableDataElement extends HTMLElement {onLoadTableData: () => void
}
const tableNormalRef = ref<LoadTableDataElement | null>(null)// 重新加载
const valueChange = () => {if(tableNormalRef.value && tableNormalRef.value.onLoadTableData) tableNormalRef.value.onLoadTableData()
}
</script>

        此时VSCode中波浪线不存在了。

3.3 null类型

        在通过ref获取组件实例中,有可能出现组件实例未获取到的情况(即为null类型),所以在valueChange函数中调用前,需判断其是否存在,再调用。否则会报错【错误提示:“tableNormalRef.value”可能为 “null”。

四、useTemplateRef

        模板引用也可以被用在一个子组件上,这种情况下引用中获得的值是组件实例。

4.1 新特性获取组件实例

        在前面讲了,使用ref方式获取组件实例,但在3.5版本后,可以通过新特性useTemplateRef来获取模板中的组件实例。我们将上面示例代码稍作修改,代码如下:

<template><div><table-normal ref="tableNormalRef"></table-normal><button @click="() => valueChange()">重新加载</button></div>
</template>
<script lang="ts" setup>
import { ref} from "vue";
// 定义新接口类型
interface LoadTableDataElement extends HTMLElement {onLoadTableData: () => void
}
const tableNormalRef = useTemplateRef('tableNormalRef')// 重新加载
const valueChange = () => {if(tableNormalRef.value && tableNormalRef.value.onLoadTableData) tableNormalRef.value.onLoadTableData()
}
</script>

4.2 类型断言处理

        使用useTemplateRef时,也会出现类型断言问题,此时上述代码会提示如下错误【类型“{}”上不存在属性“onLoadTableData”。】。

        此时,我们3.2中定义的新接口类型,指定给useTemplateRef即可,代码如下:

<template><div><table-normal ref="tableNormalRef"></table-normal><button @click="() => valueChange()">重新加载</button></div>
</template>
<script lang="ts" setup>
import { ref} from "vue";
// 定义新接口类型
interface LoadTableDataElement extends HTMLElement {onLoadTableData: () => void
}
const tableNormalRef = useTemplateRef<LoadTableDataElement>('tableNormalRef')// 重新加载
const valueChange = () => {if(tableNormalRef.value && tableNormalRef.value.onLoadTableData) tableNormalRef.value.onLoadTableData()
}
</script>

        在开发中,当遇到各种问题时,认真阅读错误提示,并根据问题逐一排查,相信大多情况能很快定位到问题,并得到解决的。


http://www.ppmy.cn/news/1536032.html

相关文章

在Docker中运行微服务注册中心Eureka

1、Docker简介&#xff1a; 作为开发者&#xff0c;经常遇到一个头大的问题&#xff1a;“在我机器上能运行”。而将SpringCloud微服务运行在Docker容器中&#xff0c;避免了因环境差异带来的兼容性问题&#xff0c;能够有效的解决此类问题。 通过Docker&#xff0c;开发者可…

前缀和(6)_和可被k整除的子数组_蓝桥杯

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 前缀和(6)_和可被k整除的子数组 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 …

Maven、Git

1. Maven 安装 &#xff08;2024.6.23&#xff09;最新版MAVEN的安装和配置教程&#xff08;超详细&#xff09;_maven安装-CSDN博客 2. 配置IDEA和Maven的关联 1. 单个关联 &#xff08;每新建一个项目都要配一次&#xff0c;不推荐&#xff09; 配置maven home path&#…

树莓派 AI 摄像头(Raspberry Pi AI Camera)教程

系列文章目录 前言 人们使用 Raspberry Pi 产品构建人工智能项目的时间几乎与我们生产 Raspberry Pi 的时间一样长。随着我们发布功能越来越强大的设备&#xff0c;我们能够支持的原生应用范围也在不断扩大&#xff1b;但无论哪一代产品&#xff0c;总会有一些工作负载需要外部…

Linux: network: sysctl: tcp_mem

文章目录 tcp_mem 说明初始化sk_prot_mem_limitstrace event__sk_mem_raise_allocated判断逻辑在pressure modeLINUX_MIB_TCPMEMORYPRESSUREStcp send totcp_mem 说明 tcp_mem - vector of 3 INTEGERs: min, pressure, max min: below this number of pages TCP is not bother…

QT调用最新的libusb库

一&#xff1a;下载libusb文件 下载最新的库的下载网站&#xff1a;https://libusb.info/ 下载&#xff1a; 解压后目录如下&#xff1a; 二&#xff1a;库文件添加QT中 根据自己的编译器选择库&#xff1a; ①将头文件中添加libusb.h ②源文件中添加libusb-1.0.lib ③添加…

深度学习---------------------------------自注意力和位置编码

目录 自注意力跟CNN、RNN对比位置编码位置编码矩阵 绝对位置信息相对位置信息总结自注意力和位置编码自注意力该部分总代码 位置编码该部分总代码 二进制表示在编码维度上降低频率该部分总代码 自注意力 给定一个由词元组成的输入序列 x 1 x_1 x1​,…, x n x_n xn​&#xff…

Steam Deck掌机可装“黑苹果” 开发者成功安装macOS 15 Sequoia

在Steam Deck掌机上运行Windows 11相对轻松&#xff0c;但要让其成功搭载“黑苹果”系统则颇具挑战性。近日&#xff0c;有博主勇于尝试&#xff0c;将macOS 15 Sequoia安装到了Steam Deck上。 开发者kaitlyn在X平台上分享道&#xff1a;“在朋友们的鼎力相助下&#xff0c;我…