当前位置: 首页 > article >正文

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

        在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.kler.cn/news/340228.html

相关文章:

  • 清韵千言APP:一款基于RNN架构并深度优化的语言模型应用
  • Gated Transformer Networks for Multivariate Time Series Classification
  • PCL 点云SUAN关键点提取
  • ADAS中的安全性功能与舒适性功能总结
  • Python可变映射类型MutableMapping
  • 单细胞转录组 —— Cell Ranger 原始数据处理
  • 【深度学习基础模型】极限学习机(Extreme Learning Machines, ELM)详细理解并附实现代码。
  • 进程的状态的理解(概念+Linux)
  • 手搓一个Eval#Datawhale组队学习大模型任务Task4
  • 问卷的二维码怎么做?教你一招快速在线制作表单二维码
  • 【基于YOLOv5的反光衣检测预警系统】可检测图片、视频、摄像头,支持GPU加速检测以及语音播报预警
  • PHP函数 strstr() 和 stristr() 有什么区别
  • 反应香精市场报告:预计2030年全球市场规模将达到264.3亿美元
  • 遇到 MySQL 中的两个 Root 用户:问题分析与解决
  • fiddler抓包18-2_导出jmeter、postman脚本(带请求头)
  • springboot宿舍管理-计算机毕业设计源码40740
  • Linux安装elasticsearch单机版
  • ai写作,五款软件助你快速写作!
  • 分治算法(6)_归并排序_交易逆序对的总数
  • 灵芝玉叶膏简介