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" 动态禁用过渡(如某些条件下不需要动画)。
相关推荐
To_OC2 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic2 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy2 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志3 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble4 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯5 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜5 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司6 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
江华森6 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http
IT_陈寒7 小时前
Redis的KEYS命令把我搞崩溃了,改用SCAN才活过来
前端·人工智能·后端