vue3+ant-design-vue

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>

进行样式穿透之后,样式就被加入进去,实现效果:条数选择间距变大

关键注意事项

  1. 必须配合 scoped :只有 <style scoped> 才需要 :deep,全局样式(无 scoped)直接写选择器即可。

  2. 避免滥用:deep 会让样式作用于子组件,过度使用可能导致样式污染(建议配合父类名精准定位,比如 .custom-pagination:deep(...))。

  3. 选择器要精准 :先通过浏览器开发者工具(F12)找到目标元素的类名(比如 Ant Design 分页的 "条数选择器" 类名是 .ant-pagination-options),再用 :deep 包裹。

  4. 若样式不生效,可在属性后加 !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> 包裹成一个根节点)。
  • 动画由 transition CSS 属性控制(也支持 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 会认为动画立即结束。
  1. 必须包裹单个根元素<Transition> 只能有一个直接子元素(列表用 <TransitionGroup>)。
  2. key 的重要性 :动态组件、路由、列表项必须加 key,否则 Vue 可能复用元素,不触发过渡。
  3. 过渡模式mode="out-in"(先出后进)、mode="in-out"(先进后出),避免组件重叠。
  4. 样式作用域 :如果用 scoped 样式,需用 ::v-deep:deep() 穿透(如修改第三方组件的过渡样式)。
  5. 禁用过渡 :通过 :disabled="true" 动态禁用过渡(如某些条件下不需要动画)。
相关推荐
华仔啊1 小时前
图片标签用 img 还是 picture?很多人彻底弄混了!
前端·html
lichong9511 小时前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端
烟袅2 小时前
作用域链 × 闭包:三段代码,看懂 JavaScript 的套娃人生
前端·javascript
风止何安啊2 小时前
收到字节的短信:Trae SOLO上线了?尝尝鲜,浅浅做个音乐播放器
前端·html·trae
抱琴_2 小时前
大屏性能优化终极方案:请求合并+智能缓存双剑合璧
前端·javascript
用户463989754322 小时前
Harmony os——长时任务(Continuous Task,ArkTS)
前端
fruge2 小时前
低版本浏览器兼容方案:IE11 适配 ES6 语法与 CSS 新特性
前端·css·es6
颜酱2 小时前
开发工具链-构建、测试、代码质量校验常用包的比较
前端·javascript·node.js
颜酱3 小时前
package.json 配置指南
前端·javascript·node.js