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

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

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

一、创建子组件(表格)

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

TypeScript 复制代码
<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文件。示例代码如下:

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

}
</script>

2.1 ref获取组件实例

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

TypeScript 复制代码
<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()函数。

TypeScript 复制代码
if(tableNormalRef.value && tableNormalRef.value.onLoadTableData) 
    tableNormalRef.value.onLoadTableData()

3.1 "never"断言

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

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

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

TypeScript 复制代码
<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类型即可。代码如下:

TypeScript 复制代码
<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来获取模板中的组件实例。我们将上面示例代码稍作修改,代码如下:

TypeScript 复制代码
<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即可,代码如下:

TypeScript 复制代码
<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>

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

相关推荐
Hello.Reader2 分钟前
全面解析 Golang Gin 框架
开发语言·golang·gin
禁默13 分钟前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood19 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
学代码的小前端21 分钟前
0基础学前端-----CSS DAY9
前端·css
Code哈哈笑22 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
joan_8525 分钟前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui
程序猿进阶25 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
qq_4336184428 分钟前
shell 编程(二)
开发语言·bash·shell
charlie11451419142 分钟前
C++ STL CookBook
开发语言·c++·stl·c++20
袁袁袁袁满42 分钟前
100天精通Python(爬虫篇)——第113天:‌爬虫基础模块之urllib详细教程大全
开发语言·爬虫·python·网络爬虫·爬虫实战·urllib·urllib模块教程