1.:deep
scoped 会限制样式仅作用于当前组件的元素,deep() 是用于穿透 scoped 样式作用域的语法,核心作用是修改子组件 / 第三方组件(如 Ant Design Vue)内部的样式。
当 <style> 标签加上 scoped 时,Vue 会给当前组件的所有元素自动添加一个唯一的属性(如 data-v-xxxxxx),样式会被编译为「选择器 + 该属性」的形式(比如 .my-class[data-v-xxxxxx])。
子组件 / 第三方组件(如 Ant Design 的 Pagination)的内部元素 不会继承这个属性,所以直接写子组件的类名(比如 .ant-pagination-options)会失效 ------ 此时需要用 :deep() 包裹选择器,让样式 "穿透" 到子组件内部。
xml
<template>
<!-- 你的分页组件 -->
<Pagination
v-model:current="pageIndex"
v-model:pageSize="pageSize"
:total="total"
show-size-changer
/>
</template>
这是没有添加样式时源代码

xml
<style lang="scss" scoped>
:deep(.ant-pagination-options) {
margin-left: 20px;
}
</style>
进行样式穿透之后,样式就被加入进去,实现效果:条数选择间距变大 
关键注意事项
-
必须配合
scoped:只有<style scoped>才需要:deep,全局样式(无scoped)直接写选择器即可。 -
避免滥用 :
:deep会让样式作用于子组件,过度使用可能导致样式污染(建议配合父类名精准定位,比如.custom-pagination:deep(...))。 -
选择器要精准 :先通过浏览器开发者工具(F12)找到目标元素的类名(比如 Ant Design 分页的 "条数选择器" 类名是
.ant-pagination-options),再用:deep包裹。 -
若样式不生效,可在属性后加
!important强制覆盖。ini<Pagination class="custom-pagination" //额外增加类名,防止使用:deep穿透造成污染 v-model:current="pageIndex" v-model:pageSize="pageSize" :total="total" :show-total="(total) => `共 ${total} 条`" /> .custom-pagination:deep(.ant-pagination-options) { margin-left: 80px; }

效果:

原本分页展示:

改自己的子组件样式
如果一个自定义子组件 <ChildComp>,其内部有类名 .child-item,父组件要修改它:
xml
<template>
<!-- 父组件中使用子组件 -->
<ChildComp />
</template>
<style scoped>
/* 穿透到 ChildComp 内部,修改 .child-item 的样式 */
:deep(.child-item) {
color: red; /* 子组件的 .child-item 文字变成红色 */
}
</style>
样式预处理器兼容(SCSS/Sass)
SCSS/Sass,:deep 的写法不变
xml
<style scoped lang="scss">
.custom-pagination {
// 嵌套写法也支持
:deep(..custom-pagination) {
margin-right: 10px;
&:hover {
border-color: red; // hover时的边框颜色
}
}
}
</style>
Vue2 与 Vue3 的写法区别
- Vue2 中是
/deep/、::v-deep、>>>(不同预处理器写法不同)。 - Vue3 在
<style scoped>中统一推荐用:deep(),兼容性更好。
Transition
Vue3 中的 <Transition> 是内置的过渡动画组件,用于给 单个元素 / 组件 的 进入 / 离开 提供平滑动画效果(如淡入淡出、滑动等)。核心原理是通过动态添加 / 移除 CSS 类名,控制动画的生命周期。
1. 过渡生命周期与类名
Vue3 中过渡类名相比 Vue2 有调整(更语义化),共 6 个核心类名(默认前缀为 v-,可通过 name 属性自定义):
| 类名 | 作用时机 | 说明 |
|---|---|---|
v-enter-from |
进入动画开始前(初始状态) | 动画开始时添加,下一帧移除 |
v-enter-active |
进入动画进行中(过渡状态) | 动画开始时添加,动画结束后移除 |
v-enter-to |
进入动画结束后(目标状态) | 下一帧添加,动画结束后移除 |
v-leave-from |
离开动画开始前(初始状态) | 动画开始时添加,下一帧移除 |
v-leave-active |
离开动画进行中(过渡状态) | 动画开始时添加,动画结束后移除 |
v-leave-to |
离开动画结束后(目标状态) | 下一帧添加,动画结束后移除 |
简单示例:
css
<!-- 币别搜索过滤框:货币符合触发搜索时调用,在预览页面显示。 Transition vue组件 实现过渡效果 -->
<Transition name="fade">
<searching
v-if="showSearchFilter"
v-model:visible="showSearchFilter"
/>
</Transition>
/* 淡入淡出过渡效果 */
.fade-enter-from {
opacity: 0; /* 初始状态:完全透明 */
}
.fade-enter-active {
transition: opacity 0.3s ease-out; /* 进入动画:0.3 秒淡出效果 */
}
.fade-leave-from {
opacity: 1; /* 离开开始状态:完全不透明 */
}
.fade-leave-active {
transition: opacity 0.3s ease-in; /* 离开动画:0.3 秒淡入效果 */
opacity: 0; /* 离开结束状态:完全透明 */
}
2. 触发条件
- 条件渲染(
v-if/v-show) - 动态组件(
<component :is="xxx">) - 路由切换(结合
vue-router) - 元素的
key变化
xml
<template>
<!-- 1. 用 Transition 包裹目标元素 -->
<Transition name="fade">
<!-- 2. 触发条件:v-if/v-show -->
<div v-if="isShow" class="box">过渡动画示例</div>
</Transition>
<button @click="isShow = !isShow">切换显示</button>
</template>
<script setup>
import { ref } from 'vue'
const isShow = ref(false) // 控制元素显示/隐藏
</script>
<!-- 3. 编写过渡动画 CSS -->
<style scoped>
.box {
width: 200px;
height: 200px;
background: #409eff;
color: white;
text-align: center;
line-height: 200px;
}
/* 进入动画:淡入 + 缩放 */
.fade-enter-from {
opacity: 0; /* 初始透明 */
transform: scale(0.8); /* 初始缩小 */
}
.fade-enter-active {
transition: all 0.5s ease; /* 过渡时长和曲线 */
}
.fade-enter-to {
opacity: 1; /* 结束不透明 */
transform: scale(1); /* 结束原尺寸 */
}
/* 离开动画:淡出 + 缩放 */
.fade-leave-from {
opacity: 1;
transform: scale(1);
}
.fade-leave-active {
transition: all 0.5s ease;
}
.fade-leave-to {
opacity: 0;
transform: scale(0.8);
}
</style>
name="fade":指定过渡类名前缀,此时类名从v-xxx变为fade-xxx(避免全局样式冲突)。- 必须包裹 单个根元素 (若需多个元素,用
<div>包裹成一个根节点)。 - 动画由
transitionCSS 属性控制(也支持animation动画)。
三、使用 Animation 动画(而非 Transition)
如果需要更复杂的动画(如循环、关键帧),可使用 CSS animation 配合 <Transition>:
xml
<template>
<Transition name="bounce">
<div v-if="isShow" class="box">动画示例</div>
</Transition>
</template>
<style scoped>
/* 定义关键帧动画 */
@keyframes bounce-in {
0% { transform: scale(0); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
@keyframes bounce-out {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(0); }
}
/* 进入动画:使用 animation */
.bounce-enter-active {
animation: bounce-in 0.5s ease;
}
/* 离开动画:使用 animation */
.bounce-leave-active {
animation: bounce-out 0.5s ease;
}
/* 可选:设置动画结束后的状态(避免闪回) */
.bounce-enter-to,
.bounce-leave-from {
transform: scale(1);
}
</style>
常见场景拓展
1. 动态组件过渡
给动态切换的组件加过渡:
xml
<template>
<Transition name="fade" mode="out-in">
<component :is="currentComponent" key="currentComponent" />
</Transition>
<button @click="currentComponent = currentComponent === 'A' ? 'B' : 'A'">
切换组件
</button>
</template>
<script setup>
import { ref } from 'vue'
import A from './A.vue'
import B from './B.vue'
const currentComponent = ref('A')
</script>
mode="out-in":过渡模式,先执行离开动画,再执行进入动画(避免两个组件重叠)。- 必加
key:确保组件切换时触发过渡。
2. 路由过渡(结合 vue-router)
给路由切换加全局过渡:
xml
<!-- App.vue -->
<template>
<router-view v-slot="{ Component }">
<Transition name="route-fade">
<component :is="Component" />
</Transition>
</router-view>
</template>
<style>
/* 路由过渡样式 */
.route-fade-enter-from,
.route-fade-leave-to {
opacity: 0;
transform: translateX(20px);
}
.route-fade-enter-active,
.route-fade-leave-active {
transition: all 0.3s ease;
}
</style>
3. 列表过渡(TransitionGroup)
<Transition> 仅支持单个元素,列表过渡需用 <TransitionGroup>(包裹多个元素,需给每个元素加 key):
xml
<template>
<TransitionGroup name="list" tag="ul">
<li v-for="item in list" :key="item.id" class="list-item">
{{ item.name }}
</li>
</TransitionGroup>
<button @click="addItem">添加项</button>
</template>
<script setup>
import { ref } from 'vue'
const list = ref([{ id: 1, name: '项1' }, { id: 2, name: '项2' }])
const addItem = () => {
list.value.push({ id: Date.now(), name: `项${list.value.length + 1}` })
}
</script>
<style scoped>
ul { list-style: none; padding: 0; }
.list-item {
margin: 10px 0;
padding: 10px;
background: #f5f5f5;
}
/* 列表项过渡样式 */
.list-enter-from {
opacity: 0;
transform: translateY(10px);
}
.list-enter-active {
transition: all 0.3s ease;
}
.list-leave-active {
transition: all 0.3s ease;
opacity: 0;
transform: translateY(-10px);
}
</style>
tag="ul":指定<TransitionGroup>渲染为<ul>标签(默认不渲染根标签)。- 每个列表项必须有唯一
key。
4.JavaScript 钩子(控制复杂动画)
如果需要通过 JS 控制动画(如回调、异步动画),可使用 <Transition> 的钩子函数:
ini
<template>
<Transition
@enter="onEnter"
@leave="onLeave"
:css="false" <!-- 禁用 CSS 过渡,完全由 JS 控制 -->
>
<div v-if="isShow" ref="boxRef" class="box">JS 控制动画</div>
</Transition>
</template>
<script setup>
import { ref } from 'vue'
const isShow = ref(false)
const boxRef = ref(null)
// 进入动画钩子
const onEnter = (el, done) => {
el.style.opacity = 0
el.style.transform = 'scale(0.8)'
// 用 requestAnimationFrame 触发动画
requestAnimationFrame(() => {
el.style.transition = 'all 0.5s ease'
el.style.opacity = 1
el.style.transform = 'scale(1)'
// 动画结束后调用 done() 通知 Vue
el.addEventListener('transitionend', done)
})
}
// 离开动画钩子
const onLeave = (el, done) => {
el.style.transition = 'all 0.5s ease'
el.style.opacity = 0
el.style.transform = 'scale(0.8)'
el.addEventListener('transitionend', done)
}
</script>
:css="false":必须设置,避免 Vue 自动添加 CSS 类名干扰。- 钩子函数的
done参数:必须在动画结束后调用(如transitionend事件),否则 Vue 会认为动画立即结束。
- 必须包裹单个根元素 :
<Transition>只能有一个直接子元素(列表用<TransitionGroup>)。 key的重要性 :动态组件、路由、列表项必须加key,否则 Vue 可能复用元素,不触发过渡。- 过渡模式 :
mode="out-in"(先出后进)、mode="in-out"(先进后出),避免组件重叠。 - 样式作用域 :如果用
scoped样式,需用::v-deep或:deep()穿透(如修改第三方组件的过渡样式)。 - 禁用过渡 :通过
:disabled="true"动态禁用过渡(如某些条件下不需要动画)。