Logo

使用Vue3 TSX 插槽,让组件复用更灵活

author
YGHub·2024-11-19·1·字数:293 字·阅读时间:1 分钟

在 Vue3 中使用 TSX 开发时,插槽的使用方式与模板语法有些不同。如何在 TSX 中优雅地使用插槽,让组件更具复用性!

一、基础用法

1. 默认插槽

jsx
// 定义组件
const Card = (props, { slots }) => {
return (
<div class="card">
<div class="card-content">
{slots.default?.()}
</div>
</div>
)
}
 
// 使用组件
const App = () => {
return (
<Card>
<div>这是默认插槽内容</div>
</Card>
)
}
 

2. 具名插槽

jsx
// 定义组件
const Layout = (props, { slots }) => {
return (
<div class="layout">
<header>{slots.header?.()}</header>
<main>{slots.default?.()}</main>
<footer>{slots.footer?.()}</footer>
</div>
)
}
 
// 使用组件
const App = () => {
return (
<Layout>
{{
header: () => <h1>页面标题</h1>,
default: () => <div>主要内容</div>,
footer: () => <div>页脚内容</div>
}}
</Layout>
)
}
 

二、作用域插槽

1. 基本使用

jsx
// 定义组件
interface ListProps {
items: string[]
}
 
const List = (props: ListProps, { slots }) => {
return (
<div class="list">
{props.items.map((item, index) => (
slots.default?.({ item, index })
))}
</div>
)
}
 
// 使用组件
const App = () => {
const items = ['苹果', '香蕉', '橙子']
return (
<List items={items}>
{({ item, index }) => (
<div class="item">
{index + 1}: {item}
</div>
)}
</List>
)
}
 

2. 类型定义

jsx
// 定义插槽类型
interface SlotProps {
item: string
index: number
}
 
interface ListSlots {
default: (props: SlotProps) => any
}
 
// 使用类型
const List = (props: ListProps, { slots }: { slots: ListSlots }) => {
return (
<div class="list">
{props.items.map((item, index) => (
slots.default({ item, index })
))}
</div>
)
}
 

三、高级用法

1. 条件渲染插槽

jsx
const Card = (props, { slots }) => {
return (
<div class="card">
{slots.title?.() && (
<div class="card-title">
{slots.title?.()}
</div>
)}
<div class="card-content">
{slots.default?.()}
</div>
{slots.footer?.() && (
<div class="card-footer">
{slots.footer?.()}
</div>
)}
</div>
)
}
 

2. 插槽默认内容

jsx
const Button = (props, { slots }) => {
return (
<button class="btn">
{slots.default?.() || '点击'}
</button>
)
}
 

3. 动态插槽

jsx
const DynamicLayout = (props, { slots }) => {
const slotNames = ['header', 'content', 'footer']
return (
<div class="dynamic-layout">
{slotNames.map(name => (
<div class={`section-${name}`}>
{slots[name]?.()}
</div>
))}
</div>
)
}
 

四、实战示例

1. 表格组件

jsx
interface TableColumn {
key: string
title: string
}
 
interface TableProps {
columns: TableColumn[]
data: any[]
}
 
const Table = (props: TableProps, { slots }) => {
return (
<table>
<thead>
<tr>
{props.columns.map(column => (
<th key={column.key}>
{slots[`header-${column.key}`]?.() || column.title}
</th>
))}
</tr>
</thead>
<tbody>
{props.data.map((row, index) => (
<tr key={index}>
{props.columns.map(column => (
<td key={column.key}>
{slots[`cell-${column.key}`]?.({ row, index }) ||
row[column.key]}
</td>
))}
</tr>
))}
</tbody>
</table>
)
}
 

2. 弹窗组件

jsx
interface ModalProps {
visible: boolean
onClose: () => void
}
 
const Modal = (props: ModalProps, { slots }) => {
return (
<Teleport to="body">
{props.visible && (
<div class="modal-wrapper">
<div class="modal">
<div class="modal-header">
{slots.header?.() || <h3>提示</h3>}
</div>
<div class="modal-content">
{slots.default?.()}
</div>
<div class="modal-footer">
{slots.footer?.() || (
<button onClick={props.onClose}>关闭</button>
)}
</div>
</div>
</div>
)}
</Teleport>
)
}
 

五、最佳实践

1. 类型定义

jsx
// 定义插槽类型
type DefaultSlotType = () => any
 
interface CustomSlots {
default?: DefaultSlotType
[key: string]: DefaultSlotType | undefined
}
 
// 组件定义
interface ComponentProps {
// props 类型
}
 
const Component = (
props: ComponentProps,
{ slots }: { slots: CustomSlots }
) => {
// 组件实现
}
 

2. 插槽工具函数

jsx
// 工具函数
const renderSlot = (slot: undefined | (() => any), defaultContent?: any) => {
return slot?.() || defaultContent
}
 
// 使用示例
const Component = (props, { slots }) => {
return (
<div>
{renderSlot(slots.default, <div>默认内容</div>)}
</div>
)
}
 

3. 组合使用

jsx
// 复杂组件示例
const ComplexComponent = (props, { slots }) => {
return (
<div class="complex-component">
{/* 条件渲染 */}
{slots.header && (
<header class="header">
{renderSlot(slots.header)}
</header>
)}
{/* 作用域插槽 */}
<main class="content">
{props.items.map((item, index) => (
renderSlot(
slots.item,
{ item, index },
<div class="default-item">{item.name}</div>
)
))}
</main>
{/* 默认内容 */}
<footer class="footer">
{renderSlot(slots.footer, <div>默认页脚</div>)}
</footer>
</div>
)
}
 

总结

在 Vue3 TSX 中使用插槽需要注意以下几点:

1.基础用法

  • 通过 slots 参数访问插槽
  • 使用可选链调用插槽函数
  • 支持具名插槽和默认插槽

2.类型支持

  • 定义清晰的插槽类型
  • 使用 TypeScript 提供类型检查
  • 注意插槽函数的返回类型

3.最佳实践

  • 提供合理的默认内容
  • 使用工具函数简化代码
  • 保持组件的可复用性
Preview

1

点个赞 ~

版权申明: © 本文著作权归YGHub所有,未经YGHub网授权许可,禁止第三方以任何形式转载和使用本文内容。