Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 插槽(Slots)与作用域插槽

插槽(Slots)与作用域插槽

插槽(Slots)是 Vue 3 中实现组件内容分发的强大机制,允许父组件向子组件注入自定义内容。普通插槽提供静态内容,而作用域插槽(Scoped Slots)则通过数据绑定实现动态交互。本节将详细讲解插槽和作用域插槽的用法、实现方式和应用场景,帮助你掌握这一组件化开发的利器。

什么是插槽?

插槽是子组件中的占位符,允许父组件在渲染时插入内容。Vue 3 支持默认插槽、具名插槽和作用域插槽。

基本用法:默认插槽

子组件(Child.vue)

<template>
  <div>
    <h2>子组件</h2>
    <slot>默认内容</slot>
  </div>
</template>

父组件

<template>
  <div>
    <Child>
      <p>来自父组件的内容</p>
    </Child>
  </div>
</template>

<script>
import Child from './Child.vue';

export default {
  components: { Child }
};
</script>
  • 效果:子组件渲染:
    子组件
    来自父组件的内容
    
  • 解析:
    • <slot> 是插槽占位符。
    • 未提供内容时,显示“默认内容”。

具名插槽

具名插槽允许多个插槽共存,使用 name 属性区分。

示例:多插槽布局

子组件(Layout.vue)

<template>
  <div>
    <header>
      <slot name="header">默认头部</slot>
    </header>
    <main>
      <slot>默认内容</slot>
    </main>
    <footer>
      <slot name="footer">默认底部</slot>
    </footer>
  </div>
</template>

父组件

<template>
  <div>
    <Layout>
      <template #header>
        <h1>自定义头部</h1>
      </template>
      <p>主要内容</p>
      <template #footer>
        <p>自定义底部</p>
      </template>
    </Layout>
  </div>
</template>

<script>
import Layout from './Layout.vue';

export default {
  components: { Layout }
};
</script>
  • 效果:
    <header><h1>自定义头部</h1></header>
    <main><p>主要内容</p></main>
    <footer><p>自定义底部</p></footer>
    
  • 解析:
    • #header 是 v-slot:header 的简写。
    • 无 name 的 <slot> 为默认插槽。

作用域插槽

作用域插槽通过子组件向父组件传递数据,实现动态内容渲染。

基本用法

子组件(List.vue)

<template>
  <ul>
    <slot v-for="item in items" :item="item" :key="item.id" />
  </ul>
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const items = reactive([
      { id: 1, name: '苹果' },
      { id: 2, name: '香蕉' }
    ]);
    return { items };
  }
};
</script>

父组件

<template>
  <div>
    <List v-slot="{ item }">
      <li>{{ item.name }} (ID: {{ item.id }})</li>
    </List>
  </div>
</template>

<script>
import List from './List.vue';

export default {
  components: { List }
};
</script>
  • 效果:
    <ul>
      <li>苹果 (ID: 1)</li>
      <li>香蕉 (ID: 2)</li>
    </ul>
    
  • 解析:
    • :item="item" 将子组件数据绑定到插槽。
    • v-slot="{ item }" 解构接收数据。

多参数传递

子组件

<template>
  <div>
    <slot :user="user" :active="isActive" />
  </div>
</template>

<script>
import { ref, reactive } from 'vue';

export default {
  setup() {
    const user = reactive({ name: 'Alice', age: 25 });
    const isActive = ref(true);
    return { user, isActive };
  }
};
</script>

父组件

<template>
  <div>
    <Child v-slot="{ user, active }">
      <p>{{ user.name }} is {{ active ? 'active' : 'inactive' }}</p>
    </Child>
  </div>
</template>

<script>
import Child from './Child.vue';

export default {
  components: { Child }
};
</script>
  • 效果:显示“Alice is active”。

高级用法

1. 默认内容与条件渲染

子组件

<template>
  <div>
    <slot name="content" :items="items">
      <p v-if="!items.length">暂无数据</p>
      <ul v-else>
        <li v-for="item in items" :key="item">{{ item }}</li>
      </ul>
    </slot>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const items = ref([]);
    return { items };
  }
};
</script>

父组件

<template>
  <div>
    <Child #content="{ items }">
      <ul>
        <li v-for="item in items" :key="item">{{ item.toUpperCase() }}</li>
      </ul>
    </Child>
  </div>
</template>
  • 效果:若 items 为空,显示“暂无数据”;否则父组件自定义渲染。

2. 与动态组件结合

<template>
  <div>
    <button @click="currentComp = 'CompA'">CompA</button>
    <button @click="currentComp = 'CompB'">CompB</button>
    <component :is="currentComp" v-slot="{ text }">
      <p>动态内容:{{ text }}</p>
    </component>
  </div>
</template>

<script>
import { ref } from 'vue';
import CompA from './CompA.vue';
import CompB from './CompB.vue';

export default {
  components: { CompA, CompB },
  setup() {
    const currentComp = ref('CompA');
    return { currentComp };
  }
};
</script>

子组件(CompA.vue)

<template>
  <slot :text="message" />
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const message = ref('来自 A');
    return { message };
  }
};
</script>
  • 效果:切换组件时动态显示不同内容。

应用场景

  1. 布局组件:
    • 使用具名插槽定义头部、主体、底部。
  2. 列表渲染:
    • 作用域插槽自定义列表项样式。
  3. 通用组件:
    • 如表格、模态框,内容由父组件决定。

示例:可复用表格

子组件(Table.vue)

<template>
  <table>
    <thead>
      <tr>
        <th v-for="col in columns" :key="col">{{ col }}</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in data" :key="row.id">
        <slot name="row" :row="row">
          <td v-for="col in columns" :key="col">{{ row[col] }}</td>
        </slot>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    columns: Array,
    data: Array
  }
};
</script>

父组件

<template>
  <div>
    <Table :columns="columns" :data="items">
      <template #row="{ row }">
        <td>{{ row.name }}</td>
        <td><button @click="edit(row.id)">编辑</button></td>
      </template>
    </Table>
  </div>
</template>

<script>
import { reactive } from 'vue';
import Table from './Table.vue';

export default {
  components: { Table },
  setup() {
    const columns = ['name', 'action'];
    const items = reactive([
      { id: 1, name: '苹果' },
      { id: 2, name: '香蕉' }
    ]);
    const edit = (id) => console.log('编辑:', id);
    return { columns, items, edit };
  }
};
</script>
  • 效果:表格显示名称和操作按钮,点击触发事件。

注意事项

  1. 作用域隔离:
    • 插槽内容在父组件作用域渲染,访问父组件数据。
  2. 性能:
    • 过多插槽可能增加复杂度,合理规划。
  3. 命名冲突:
    • 具名插槽确保唯一性。

总结

插槽和作用域插槽为 Vue 3 的组件化开发提供了极大的灵活性。默认插槽和具名插槽适合内容分发,作用域插槽则赋予父组件动态定制能力。通过这些机制,你可以打造高度可复用的组件。下一节将探索组件库开发,带你进入更高层次的实践!

Last Updated:: 2/24/25, 10:48 AM