效果:
快来学习:
Vue 3 Composition API 和
script setup
语法
- Composition API:Vue 3 引入的 Composition API 相比 Vue 2 的 Options API 提供了更灵活的代码组织方式。使用
setup
函数,可以将组件的所有功能和逻辑集中在一起,方便复用。script setup
语法:Vue 3 的<script setup>
是 Composition API 的简化写法,将setup
函数的代码直接编写在<script setup>
中,自动将定义的变量和方法暴露给模板。响应式状态管理
ref
:ref
用于定义单一响应式变量,当变量改变时,Vue 会自动更新模板。示例中的tabCount
和activeTabId
使用ref
定义。reactive
:reactive
用于定义复杂的数据结构,如对象或数组。示例中的tabs
使用reactive
定义,管理页签数据,数组变化时页面会自动更新。页面渲染和模板指令
v-for
:Vue 的v-for
指令用于循环渲染数组的每个元素。这里使用v-for="tab in tabs"
循环渲染所有页签和内容。- 动态类绑定:
v-bind:class
或简写:class
用于动态绑定类名,['tab', { active: activeTabId === tab.id }]
实现了激活状态的切换。- 事件处理:使用
@click
绑定点击事件,点击时触发相应的激活或关闭操作。Vue 提供的事件修饰符.stop
可以阻止事件冒泡,防止触发父级元素的点击事件。事件冒泡与
@click.stop
- 事件冒泡:HTML 中,点击事件会逐级向上传递到父级元素。这种行为在需要精准触发特定操作时会带来困扰。
@click.stop
修饰符:Vue 提供.stop
修饰符阻止事件冒泡,确保关闭按钮仅触发关闭操作而不会激活页签。Scoped CSS
- Scoped 样式:
<style scoped>
限制样式只作用于当前组件,防止影响其他组件,保证了组件的样式独立性。
代码:
<template><!-- 页签容器 --><div class="tabs-container"><!-- 页签列表 --><div class="tabs"><!-- 使用 v-for 循环渲染每一个页签 --><div v-for="tab in tabs" :key="tab.id" :class="['tab', { active: activeTabId === tab.id }]"@click="activateTab(tab.id)">{{ tab.label }}<!-- 关闭按钮,阻止事件冒泡以防触发 activateTab --><span class="close-btn" @click.stop="closeTab(tab.id)">x</span></div></div><!-- 添加新页签的按钮 --><button style="width: 50px; height: 30px;" @click="addTab">+</button></div><!-- 页签内容容器 --><div class="content-container"><!-- 循环渲染每个页签对应的内容区域 --><div v-for="tab in tabs" :key="tab.id" :class="['tab-content', { active: activeTabId === tab.id }]">{{ tab.content }}</div></div>
</template><script setup>
import { reactive, ref } from 'vue'// 初始化页签计数器,用于给每个新页签分配唯一 ID
let tabCount = ref(0)
// 定义一个响应式数组,用于存储所有的页签信息
const tabs = reactive([])
// 定义当前激活页签的 ID
const activeTabId = ref(null)// 添加新页签的函数
function addTab () {tabCount.value++ // 页签计数加一const newTab = {id: tabCount.value, // 分配唯一 IDlabel: `页签 ${tabCount.value}`, // 页签的标签名称content: `这是页签 ${tabCount.value} 的内容` // 页签的内容}tabs.push(newTab) // 将新页签加入页签数组activateTab(newTab.id) // 激活新添加的页签
}// 激活指定页签的函数
function activateTab (tabId) {activeTabId.value = tabId // 更新当前激活的页签 ID
}// 关闭指定页签的函数
function closeTab (tabId) {// 找到要关闭的页签的索引const index = tabs.findIndex((tab) => tab.id === tabId)if (index !== -1) {tabs.splice(index, 1) // 从数组中移除该页签// 如果关闭的是当前激活的页签if (activeTabId.value === tabId && tabs.length > 0) {activeTabId.value = tabs[0].id // 激活第一个页签} else if (tabs.length === 0) {activeTabId.value = null // 如果没有页签,重置 activeTabId 为 null}}
}
</script><style scoped>
/* 样式 */
.tabs {display: flex;align-items: center;margin-bottom: 10px;
}.tabs-container {display: flex;align-items: center;justify-content: start;
}.tab {position: relative;padding: 10px 20px;margin-right: 5px;background-color: #eee;cursor: pointer;border-radius: 5px;
}.tab.active {background-color: #ddd;/* 激活状态的页签背景色 */
}.close-btn {position: absolute;right: 5px;top: 5px;font-size: 14px;cursor: pointer;color: red;
}.tab-content {display: none;/* 默认隐藏页签内容 */
}.tab-content.active {display: block;/* 仅激活的页签内容显示 */height: 200px;width: 300px;color: #fff;background-color: green;
}
</style>