Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Vue 3 + TypeScript 组件封装艺术:从原理到实践

Vue 3 + TypeScript 组件封装艺术:从原理到实践

作者头像
编程小白狼
发布于 2025-04-26 06:32:27
发布于 2025-04-26 06:32:27
23001
代码可运行
举报
文章被收录于专栏:编程小白狼编程小白狼
运行总次数:1
代码可运行

在现代前端开发中,组件化开发已成为主流模式。Vue 3 配合 TypeScript 的组合,为我们提供了更强大的类型系统和更优秀的开发体验。本文将深入探讨如何基于 Vue 3 和 TypeScript 进行高质量的组件封装,并通过实际案例展示最佳实践。

一、为什么要封装组件?

1.1 组件封装的优势 • 代码复用:避免重复造轮子

• 统一维护:一处修改,多处生效

• 团队协作:明确接口规范,降低沟通成本

• 类型安全:TypeScript 提供完善的类型检查

1.2 Vue 3 组合式 API 的优势

代码语言:javascript
代码运行次数:1
运行
AI代码解释
复制
// 传统选项式 API vs 组合式 API
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}

// 组合式 API
import { ref } from 'vue'
const count = ref(0)
const increment = () => count.value++

二、基础组件封装实战

2.1 创建一个按钮组件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// src/components/BasicButton.vue
<template>
  <button 
    :class="['basic-btn', type]"
    :disabled="disabled"
    @click="handleClick"
  >
    <slot></slot>
  </button>
</template>

<script lang="ts" setup>
import { withDefaults } from 'vue'

interface Props {
  type?: 'primary' | 'danger' | 'default'
  disabled?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  type: 'default',
  disabled: false
})

const emit = defineEmits<{
  (e: 'click', event: MouseEvent): void
}>()

const handleClick = (event: MouseEvent) => {
  if (!props.disabled) {
    emit('click', event)
  }
}
</script>

<style scoped>
.basic-btn {
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s;
  
  &.primary {
    background-color: #409eff;
    color: white;
  }
  
  &.danger {
    background-color: #f56c6c;
    color: white;
  }
  
  &[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
  }
}
</style>

2.2 使用组件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>
  <BasicButton 
    type="primary" 
    @click="handleClick"
  >
    提交表单
  </BasicButton>
</template>

<script lang="ts" setup>
import BasicButton from '@/components/BasicButton.vue'

const handleClick = (event: MouseEvent) => {
  console.log('按钮被点击', event)
}
</script>

三、高级组件封装技巧

3.1 带插槽和复杂逻辑的模态框组件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// src/components/AdvancedModal.vue
<template>
  <transition name="fade">
    <div class="modal-mask" v-show="modelValue">
      <div class="modal-container">
        <div class="modal-header">
          <h3>{{ title }}</h3>
          <button class="close-btn" @click="close">×</button>
        </div>
        
        <div class="modal-body">
          <slot name="body"></slot>
        </div>
        
        <div class="modal-footer">
          <slot name="footer">
            <BasicButton @click="close">取消</BasicButton>
            <BasicButton type="primary" @click="confirm">确认</BasicButton>
          </slot>
        </div>
      </div>
    </div>
  </transition>
</template>

<script lang="ts" setup>
import { watch } from 'vue'
import BasicButton from './BasicButton.vue'

interface Props {
  modelValue: boolean
  title?: string
}

const props = withDefaults(defineProps<Props>(), {
  title: '提示'
})

const emit = defineEmits<{
  (e: 'update:modelValue', value: boolean): void
  (e: 'confirm'): void
  (e: 'close'): void
}>()

const close = () => {
  emit('update:modelValue', false)
  emit('close')
}

const confirm = () => {
  emit('confirm')
  close()
}

// 监听外部变化
watch(() => props.modelValue, (newVal) => {
  if (newVal) {
    document.body.style.overflow = 'hidden'
  } else {
    document.body.style.overflow = ''
  }
})
</script>

<style scoped>
.modal-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.modal-container {
  background: white;
  border-radius: 8px;
  width: 50%;
  min-width: 300px;
  max-width: 600px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
}

.modal-header {
  padding: 16px;
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.modal-body {
  padding: 16px;
}

.modal-footer {
  padding: 16px;
  border-top: 1px solid #eee;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.close-btn {
  background: none;
  border: none;
  font-size: 20px;
  cursor: pointer;
}

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

3.2 使用示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>
  <button @click="showModal = true">打开模态框</button>
  
  <AdvancedModal 
    v-model="showModal" 
    title="删除确认"
    @confirm="handleConfirm"
  >
    <template #body>
      <p>确定要删除这条数据吗?此操作不可撤销。</p>
    </template>
    
    <template #footer>
      <BasicButton @click="showModal = false">取消</BasicButton>
      <BasicButton type="danger" @click="handleConfirm">永久删除</BasicButton>
    </template>
  </AdvancedModal>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import AdvancedModal from '@/components/AdvancedModal.vue'
import BasicButton from '@/components/BasicButton.vue'

const showModal = ref(false)

const handleConfirm = () => {
  console.log('执行删除操作')
  // 实际业务逻辑...
}
</script>

四、组件设计原则

4.1 单一职责原则 每个组件应该只关注一个特定的功能点

4.2 受控与非受控组件 • 受控组件:状态由父组件完全控制

• 非受控组件:内部维护自身状态

4.3 接口设计规范

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface Props {
  // 基本类型
  size?: 'small' | 'medium' | 'large'
  disabled?: boolean
  
  // 复杂类型
  data?: Record<string, any>[]
  
  // 函数类型
  formatter?: (value: any) => string
}

interface Emits {
  (e: 'change', value: string): void
  (e: 'update:modelValue', value: boolean): void
}

interface Slots {
  default?: () => VNode[]
  header?: (props: { title: string }) => VNode[]
}

五、TypeScript 高级技巧

5.1 泛型组件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 可复用的列表组件
interface ListProps<T> {
  data: T[]
  itemKey: keyof T
}

defineProps<ListProps>()

5.2 类型推断优化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 自动推断 props 默认值类型
const props = withDefaults(defineProps<{
  size?: 'small' | 'medium'
}>(), {
  size: 'medium' // 自动推断为 'small' | 'medium'
})

5.3 全局组件类型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// components.d.ts
declare module 'vue' {
  export interface GlobalComponents {
    BasicButton: typeof import('./components/BasicButton.vue')['default']
    AdvancedModal: typeof import('./components/AdvancedModal.vue')['default']
  }
}

六、测试与文档

6.1 单元测试示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { mount } from '@vue/test-utils'
import BasicButton from '@/components/BasicButton.vue'

test('emits click event when clicked', async () => {
  const wrapper = mount(BasicButton, {
    slots: {
      default: 'Click me'
    }
  })
  
  await wrapper.trigger('click')
  expect(wrapper.emitted()).toHaveProperty('click')
})

6.2 组件文档(使用 Vitepress)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
## BasicButton 基础按钮

### 基础用法

```vue
<template>
  <BasicButton type="primary">主要按钮</BasicButton>
</template>
```

### Props

| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|-------|
| type | 按钮类型 | `'primary' | 'danger' | 'default'` | `'default'` |
| disabled | 是否禁用 | `boolean` | `false` |

### Events

| 事件名 | 说明 | 回调参数 |
|-------|------|---------|
| click | 点击事件 | `MouseEvent` |

结语

通过本文的讲解,我们了解了 Vue 3 和 TypeScript 在组件封装方面的强大能力。从基础按钮到复杂模态框,从类型定义到测试文档,一个完整的组件开发生命周期已经清晰地展现在我们面前。

记住,好的组件设计应该是: • 直观的:接口设计符合直觉

• 灵活的:通过插槽和 props 提供定制能力

• 健壮的:完善的类型定义和错误处理

• 可测试的:易于编写单元测试

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【愚公系列】《AIGC辅助软件开发》016-AI辅助前端编程:利用ChatGPT在前端开发中快速生成Vue组件
文章地址:https://cloud.tencent.com/developer/article/2474495
愚公搬代码
2024/12/08
2210
Vue3 结合 TypeScript 项目开发使用指南及组件封装实操方法
这些方法与Vue3+TypeScript项目结合,可以帮助你高效构建高质量的前端应用。
用户2102001
2025/05/29
1860
Vue3 结合 TypeScript 项目开发使用指南及组件封装实操方法
Vue 单点登录组件封装方法及使用指南详解
这些组件和方法可以作为企业级应用单点登录功能的基础,根据实际需求可以进一步扩展和优化。
用户2102001
2025/05/30
870
Vue 单点登录组件封装方法及使用指南详解
Vue3 过10种组件通讯方式
众所周知,Vue.js 中一个很重要的知识点是组件通信,不管是业务类的开发还是组件库开发,都有各自的通讯方法。
德育处主任
2022/04/17
2K0
Vue3 过10种组件通讯方式
面试题分享之封装一个弹框组件
题目为: vue3中实现一个父页面的弹窗功能,描述显隐和传参的实现逻辑,(效果截图和关键代码截图)
心安事随
2024/07/29
1320
面试题分享之封装一个弹框组件
面试官:用Vue3.0 写过组件吗?如果想实现一个 Modal你会怎么设计?
现在有一个场景,点击新增与编辑都弹框出来进行填写,功能上大同小异,可能只是标题内容或者是显示的主体内容稍微不同
@超人
2021/03/18
1.2K0
面试官:用Vue3.0 写过组件吗?如果想实现一个 Modal你会怎么设计?
【摸鱼加钟】- 组件封装之Slots、Emit和Props穿透
组内多人共同开发时免不了基于某UI库二次封装组件来适应项目业务场景的情况,但不知道大家有没有遇到过需要兼容部分或者穿透子组件全部Props或者Slots的情况,这种时候如果针对每一个Props或者Slots去单独处理穿透不仅费时费力而且代码会越来越臃肿难以维护,所以想在这里通过一个简单的例子来对比一下Slots、Props、Emit的各种穿透方案
Gnod
2022/09/23
1K0
关于 Vue3 + setup + ts 使用技巧的总结
ref 一般用于基本的数据类型,比如 string,boolean ,reactive 一般用于对象 ref 的地方其实也是调用的 reactive 实现的。
前端达人
2023/08/31
1.1K0
关于 Vue3 + setup + ts 使用技巧的总结
前端系列14集-Vue3-setup
页面浏览量(Page View,PV)和访客数(Unique Visitors,UV)
达达前端
2023/10/08
5330
前端系列14集-Vue3-setup
Vue3 实现小米商城官网组件使用与封装详细指南
通过以上组件封装和使用方法,您可以完整构建一个功能丰富、交互友好的小米商城官网。这些组件设计遵循了Vue3的最佳实践,具有良好的可扩展性和可维护性,可以根据实际需求进行进一步定制和扩展。
用户2102001
2025/05/25
670
Vue3 实现小米商城官网组件使用与封装详细指南
Vue3的花样样式还不会?看看老前端是怎么玩儿的~
在 Vue2 组件中,设置一个全局样式,我们通常是新建一个 <style> 标签,如:
淼学派对
2023/10/14
5040
Vue3的花样样式还不会?看看老前端是怎么玩儿的~
学习 Vue 3 全家桶 - 组件化
如 <button> 是浏览器封装好的组件,Vue 支持自定义组件,把一个功能的模板(template)封装在一个 .vue 文件中,方便在项目中复用整个组件的代码。
Cellinlab
2023/05/17
4330
学习 Vue 3 全家桶 - 组件化
Vue3 内置组件学习
我们用 vite 搭建一个 Vue3 + TS 项目,我会使用 <script setup lnag="ts"> 和<script lang="ts"> 混合编程的方式来实现
Gorit
2021/12/05
5670
Vue3 内置组件学习
vue3.0 Composition API 上手初体验 普通组件的开发与使用
通过前面的章节的讲解,我相信大家对于 vue 3.0 的新特性的基本使用,已经没有问题了。但是新的问题来了,组件是怎么玩的呢?
FungLeo
2020/05/26
6250
Vue3学习笔记(四)——组件、生命周期
如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展,但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。如果我们将一个个功能块拆分后,就可以像搭建积木一下来搭建我们的项目。
张果
2022/11/14
1.5K0
Vue3学习笔记(四)——组件、生命周期
腾讯二面vue面试题总结
对象内部通过 defineReactive 方法,使用 Object.defineProperty 来劫持各个属性的 setter、getter(只会劫持已经存在的属性),数组则是通过重写数组7个方法来实现。当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)
bb_xiaxia1998
2022/11/18
7670
前端系列15集-watch,watchEffect,eventBus
watchEffect,它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
达达前端
2023/10/08
5560
🎉一个demo体验Vue3.3+TypeScript所有新功能🎉
由于最新的功能defineModel是实验特性,需要在vite.config.js里开启,另外需要开启解构props响应式功能
萌萌哒草头将军
2023/06/18
6060
🎉一个demo体验Vue3.3+TypeScript所有新功能🎉
【BlogAdmin升级3】组件通讯与引用
defineProps原理: 就是编译阶段的一个标识,实际编译器解析时,遇到后就会进行编译转换
老张的哲学
2024/02/22
1400
【BlogAdmin升级3】组件通讯与引用
【Vuejs】1286- 分享 15 个 Vue3 全家桶开发的避坑经验
最近入门 Vue3 并完成 3 个项目,遇到问题蛮多的,今天就花点时间整理一下,和大家分享 15 个比较常见的问题,基本都贴出对应文档地址,还请多看文档~ 已经完成的 3 个项目基本都是使用 Vue3 (setup-script 模式)全家桶开发,因此主要分几个方面总结:
pingan8787
2022/04/14
6.5K0
【Vuejs】1286- 分享 15 个 Vue3 全家桶开发的避坑经验
推荐阅读
相关推荐
【愚公系列】《AIGC辅助软件开发》016-AI辅助前端编程:利用ChatGPT在前端开发中快速生成Vue组件
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验