vue template 转 jsx 写法及TS类型应用

news/2025/3/10 21:05:52/文章来源:https://www.cnblogs.com/jsonq/p/18686890

vue 的响应式数据 + jsx 开发体验简直不要太好,心智负担确实小,简直完爆 react(纯属个人暴论),不足的地方就是生态了,这点确实比不过 react。

本文更侧重于 TS 类型的写法,毕竟初次接触 vue jsx 时,实在对其 TS 类型声明很不顺手。要说 vue 模板语法哪些 API 不能在 jsx 中使用,也就是一些 define 宏函数了,其它都正常使用。

v-model 双向绑定

注意:在 jsx 场景下,仍然可以使用 v-model,但是不能使用 v-model.lazy 等修饰符

网上有使用 value + event 事件的写法,都可以,论简单还是 v-model

template 写法

<script setup lang="ts">
const inputVal = ref('');
</script><template><input v-model="inputVal" /><div>{{ inputVal }}</div>
</template>

JSX 写法:ref 响应式值必须使用 .value 访问,这点和模板语法的 script 标签中使用规则一致

export default defineComponent({name: 'ChildJSXComp',setup() {const inputVal = ref('');return () => (<><input v-model={inputVal.value} /><div>{inputVal.value}</div></>);},
});

Props

基础例子

最基础的例子,子组件接收一个 message 属性,并限制类型为 string | number

template 写法

<script setup lang="ts">
interface MyProps {message?: string | number;
}
const props = defineProps<MyProps>();
</script><template>{{ props.message }}
</template>

jsx 写法:使用 PropType 可以对入参类型做进一步的类型约束

export default defineComponent({name: 'ChildJSXComp',props: {message: { type: [String, Number] as PropType<string | number> },},setup(props) {return () => <>{props.message}</>;},
});

PropType

  • 在 vue 模板的 setup 语法糖写法下,props 接收直接使用 const props = defineProps<MyProps>() 定义即可
  • 在 jsx 中,不能直接定义 TS 类型,需要使用 类型标注 + PropType 实现。

PropType 在 JSX 中可以对 props 的接收类型做更细致的类型约束

const selectorProps = {// 细化 Array 类型为 SelectorOption[]options: { type: Array as PropType<SelectorOption[]>, default: () => [] },// 细化 Object 为 { label: string; value: string }fieldNames: {type: Object as PropType<{ label: string; value: string }>,default: () => ({ label: 'label', value: 'value' }),},// 细化 Fuction 类型为 (value: string) => voidonSearch: { type: Function as PropType<(value: string) => void> },
}export default defineComponent({// ...props: selectorProps,setup(props) {// ...},
});

根据 props 的类型标注生成 TS 类型

之前使用的 props 标注是属于参数校验,是 Schema 数据,而非 TS。
如果其它组件基于此组件进行二次封装,需要该组件的 TS 类型,此时可以使用 ExtractPublicPropTypes 来将 props 类型标注转为 TS 类型,这样其它组件就可以使用了。

image

对于 vue 版本低于 3.3 的,可以使用 ExtractPropTypes,个人感觉两者的关系就是 type ExtractPublicPropTypes = Patrial<ExtractPropTypes>

props 传递事件

个人更推荐使用 props 来传递事件,而非 emits 属性接收,原因如下:

  • emits 接收事件在 eslint 下表现并不友好(不禁用某些规则的前提下)
  • emits 接收事件,最终也可以合并到 props 上,通过 props 使用

对于第一点,下一小节会讲 emits 的类型定义,对于第二点,如图所示:

image

虽然是在 emits 属性上定义的事件,但是 props 依然可以访问,那为什么不直接在 props 上定义呢? ant-design-vue 就是把事件写在了 props 中,但是也有不少组件库依然写在 emits 里,看个人习惯吧。

可能是为了区分,事件是事件(emits),属性是属性(props),不让属性和事件混在一起,从代码的结构上来说更严谨。

emits 事件接收

template 写法,使用 defineEmits

<script setup lang="ts">
interface SelectorEmits {(e: 'cancel'): void;(e: 'search', value: string): void;
}
// 3.3 + 提供的事件类型简写
// interface SelectorEmits {
//   cancel: [];
//   select: [value: string];
// }
const emit = defineEmits<SelectorEmits>();const clickButton = () => {emit('search', '1');
};
</script><template><button @click="clickButton"></button>
</template>

jsx 写法,使用 emits

  • emits 以字符串数组定义事件,则无法对事件进行详细的类型约束
export default defineComponent({// ...emits: ['search', 'cancel'],setup(props, { emit }) {const clickButton = () => {emit('search', "1");};return () => <button onClick={clickButton}>Test</button>;},
});

image

  • emits 标注 TS 类型,将 emits 换成对象写法
  emits: {search: (value: string) => true,cancel: () => true,},

这种写法在 eslint 中会出现一些问题,例如 search 事件,会报 “声明值未使用” 的问题,所以我推荐在 props 中定义事件,当然也可以全局禁用该规则,就不会出现这个报错问题。

image

slot 插槽

在 jsx 中写插槽时,没有类型提示,想要存在类型提示,只能手动标注类型

子组件接收插槽(默认插槽和传参插槽)

template 写法,使用 defineSlots 对插槽的类型进行标注,同时 IDE 也有友好的类型提示。

<script setup lang="ts">
import type { VNode } from 'vue';interface MySlots {default?(): VNode;list?(scoped: { list: string[] }): VNode;
}
defineSlots<MySlots>();
</script><template><header>Header</header><main><slot></slot></main><footer><slot name="list" :list="['1', '2', '3', '4']"></slot></footer>
</template>

jsx 写法,使用 slots 属性接收,所有插槽都是以函数调用方式渲染

slots 的 TS 写法和 props 类似,都是通过 as 来进一步约束类型

export default defineComponent({name: 'ChildComp',slots: Object as SlotsType<MySlots>,setup(props, { slots }) {return () => (<><header>Header</header><main>{slots.default?.()}</main><footer>{slots.list?.({ list: ['1', '2', '3', '4'] })}</footer></>);},
});

父组件使用插槽(默认插槽和传参插槽)

template 写法

  <ChildComp><template #default> Parnt Content </template><template #list="scoped"><p v-for="item in scoped.list" :key="item">{{ item }}</p></template></ChildComp>

使用 defineSlots 的好处就是拥有更完整、更友好的 TS 类型提示。

image

jsx 有两种使用插槽的写法

写法一: 使用 v-slots

export default defineComponent({name: 'ParentComp',setup() {return () => (<ChildJSXCompv-slots={{default: () => <div>Parnt Content</div>,list: (scoped: { list: string[] }) => {return scoped.list.map((item) => <p key={item}>{item}</p>);},}}/>);},
});

v-slots 接收的是一个对象,对象的 key 就是插槽名,value 就是一个函数。
这也就是为什么在 jsx 子组件使用插槽是 slots.default?.() 的原因:因为传入的插槽就是函数,调用函数才能得到 VNode

写法二: 组件标签内写插槽,用闭合标签的内容来代替 v-slots,其实就是把 v-slots 的内容粘贴到闭合标签中

      <ChildJSXComp>{{default: () => <div>Parnt Content</div>,list: (scoped: { list: string[] }) => {return scoped.list.map((item) => <p key={item}>{item}</p>);},}}</ChildJSXComp>

如果只传递默认插槽,也可以用最常规的写法,闭合标签内写 dom 元素

      <ChildJSXComp><div>Parnt Content</div>,</ChildJSXComp>

v-bind 动态绑定多个值

先说结论:多个值的绑定情况下,vue 的 v-bind="myObject" = jsx 的 {...myObject}

这里就以 useuse 的 useVirtualList 为例

image

以上的 v-bind 写法换成 jsx 就如下所示

image

由于 wrapperProps 是一个 computed 计算结果,所以在使用时加 .value

其它

组件别名命名

注意:该命名是在 vue devtool 中查看使用的,而非代码中组件的引用别名

在 vue 3.3+ 之后,官方提供了 defineOptions 来进行组件的别名命名(默认文件名为组件名),方便 vue devtools 中调试查看,毕竟没人愿意打开调试工具,一看组件标签名都是 Index

template 写法

<script setup lang="ts">
defineOptions({ name: 'MyCompentName' });
</script>// vue 3.3 之前需要额外的 script 标签定义
<script>
export default {name: "MyCompentName"
}
</script>

jsx 写法

export default defineComponent({name: 'MyCompentName',
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/873675.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

团队任务分配与高效执行应该如何做?从这些方面合理调配

本文聚焦团队任务分配与高效执行,深度剖析团队协作中任务分配不合理、沟通协作障碍、目标不明确、缺乏有效监督反馈等痛难点,阐述团队协作的必要性,进而给出合理分配任务、加强沟通协作等解决方法,还推荐了相关工具,为提升团队效能提供指引 。一、团队协作的痛难点 (一)…

2025.1.23

今天正式开始YOLOv8的相关学习。 YOLOv8的架构设计主要体现在以下几个方面:1. 改进的特征提取网络    YOLOv8在特征提取网络方面进行了显著改进,采用了更深、更宽的网络结构,以提高对复杂场景的处理能力。CSPNet(Cross Stage Partial Network):    CSPNet的引入有…

Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环,SVC系统调用拦截。

Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环,SVC系统调用拦截。Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环 —— SVC系统调用拦截。☞ Github: https://www.github.com/iofomo/abyss ☜ 由于我们虚拟化产品的需求,需要支持…

Seq 日志: .NET 应用程序中集成与快速入门

Seq 是一个功能强大且易于使用的日志服务器,特别适合用于集中管理和可视化 .NET 应用程序的日志。它支持结构化日志记录,提供实时查询和分析功能,帮助开发者快速定位和解决问题。 安装和配置 SeqWindows 部署: 可以从 Seq 下载页面:https://datalust.co/download 获取最新…

折腾笔记[11]-使用rust进行直接法视觉里程计估计

使用rust实现了一个完整的直接法视觉里程计系统,能够通过比较两幅图像中的像素强度来估计相机的运动。它通过单层和多层的优化策略,结合图像金字塔和并行计算,提高了位姿估计的精度和效率。最终,代码输出了优化后的相机位姿变换矩阵,并可视化了投影点的位置。摘要 使用rus…

5 链路层

5 链路层 5.1 概述 节点(node):运行链路层协议的设备(主机、路由器、交换机、WiFi接入点) 链路(link):沿着通信路径连接相邻节点的通信信道 数据链路层(data link layer):将数据报封装在链路层帧(frame)中通过通信链路从一个节点传输到另一个节点 链路层提供的服务…

【动态规划】01背包专题

01背包在恰好等于的情况下求最小物品数 MELON的难题每个物品(石头)的价值w[i]就是其自己的个数,为1 体积题目已给出。 状态定义:f[i][j]表示在前i个物品中选,且体积总和恰好等于j需要的物品个数的最小值 初始化: f[i][0] = 0 , 1 <= i <= n f[0][j] = INF, 1 <=…

阿里开源语音克隆CosyVoice2 整合包

CosyVoice2 win整合包 语音克隆CosyVoice2 链接:https://pan.quark.cn/s/5e75615a5cd4 修改webui.py默认值: # 修改默认推理模式 mode_checkbox_group = gr.Radio(choices=inference_mode_list, label=选择推理模式, value=inference_mode_list[1])# 修改随机推理种子 seed =…

KeyClicker 为用户带来真实键盘打字声音体验的应用,再现机械键盘与打字机的打字感受

如果你是一名作家,或者对打字机的声音情有独钟,KeyClicker 将是你的理想选择。许多作家认为,打字机的声音能让他们更专注、更有创作灵感。虽然实体打字机的魅力独特,但它缺乏现代设备的便捷功能,例如高效的编辑与数字化操作。而使用 KeyClicker,你既能享受打字机的经典声…

gdb调试小技巧——多个窗口显示

先用tty显示需要显示的终端的序号 ┌──(root㉿kali)-[~] └─# tty /dev/pts/3然后 vim ~/.gdbinit在文件后加一行 set context-output /dev/pts/2这里数字就是tty显示的数字 设置好之后打开gdb时就可以了

道路流量监测摄像机

道路流量监测摄像机是一种结合了监控摄像技术和交通管理的先进设备,旨在通过实时监测和分析道路上车辆的行驶情况,收集交通流量数据并进行统计分析。这种摄像机在城市交通管理、道路规划、交通安全等领域有着广泛的应用前景。道路流量监测摄像机是一种结合了监控摄像技术和交…

占用消防通道监测摄像机

占用消防通道监测摄像机是一种结合了智能分析技术和监控技术的先进设备,在预防火灾事故和保障人员安全方面具有重要意义。随着社会对安全意识不断提高以及相关法规标准日益完善,相信这种先进设备将会在更多领域得到广泛应用,并为我们创造更加安全、有序的公共环境。占用消防…