如何进行组件封装

最近准备面试了,想重新回顾一下组件封装的一些要点

一、输入(props)和输出(emits)

  1. 举一个通用按钮(BaseButton.vue)的例子

我们要封装一个按钮,支持:

  • 类型:primary、danger、default
  • 禁用状态
  • 点击事件
javascript 复制代码
<template>
    <button class="base-btn" 
        :class="[`base-btn--${type}`]" 
        :disabled="disabled" 
        @click="handleClick" 
    > 
        <!-- 默认插槽:让父组件可以传入文字或图标 --> 
        <slot></slot> 
    </button> 
  
  
<script setup>
import { computed } from 'vue'

// 1. 定义输入
const props = defineProps({
    type: {
        type: String,
        default: 'default', // 默认类型
        validator: (value) => ['primary', 'danger', 'default'].includes(value)  //简单的值校验
    },
    disabled: {
        type: Boolean,
        default: false
    }
})

// 2. 定义输出
const emit = defineEmits(['click', 'otherAAction'])

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

<style scoped>
/* 类型修饰符 */ 
.base-btn--primary { background-color: #409eff; color: white; } 
.base-btn--danger { background-color: #f56c6c; color: white; } 
.base-btn--default { background-color: #fff; border: 1px solid #dcdfe6; color: #606266; }

...
</style>

父组件使用:

javascript 复制代码
<template>
    <div>
        // 使用primary类型
        <BaseButton type="primary" @click="saveData">保存</BaseButton>
        // 使用danger类型,禁用
        <BaseButton type="danger" :disabled="true">保存</BaseButton>
    </div>
</template>

<script setup>
import BaseButton ...
const saveData = () => {
    console.log("保存数据...")
}
</script>

这里就是简单的组件封装,主要是体现props和emits的作用

二、双向绑定

  1. 举一个通用输入框(BaseInput.vue)的例子,这里仅展示核心代码,其余不过多展示
javascript 复制代码
<template>
    <input
        :value="modelValue"
        @input="handleInput"
    />
</template>

<script setup>
// 1. v-model本质是接受一个modelValue属性
const props = defineProps({
    modelValue:{
        type: [String, Number],
        defalut: ''
    },
})

// 2. v-model修改值时,会触发 update:modelValue 事件
const emit = defineEmits(['update:modelValue'])
</script>

三、属性透传⭐⭐⭐

  1. 属性透传主要在组件二次封装中使用,尤其是在UI方面对 Element Plus、Ant Design 等组件库的适应再封装
  2. Vue 3 有一个默认行为:如果子组件只有一个根元素,那么父组件传过来的、子组件没声明的属性,会自动加到这个根元素上。例如:
javascript 复制代码
<template>
    // 这里el-button就是根元素
    <el-button>
        <slot></slot>
    </el-button>
</template>

<script setup>
// 这里故意不声明 size、loading、type 等属性
</script>

在父组件中

javascript 复制代码
<template>
<!-- 父组件传了 size 和 loading,但 MyButton 自己没声明 --> 
    <MyButton size="large" loading type="primary">
        点击我 
    </MyButton>
</template>

因为只有根元素,Vue会把多余的属性自动透传到根元素,等效于

javascript 复制代码
<el-button size="large" loading type="primary">
    点击我
</el-button>
  1. 手动透传($attrs) 修改一下子组件MyButton,在最外层包装div。
    v-bind=...这里相当于精准投放,把父组件传递的属性精确的传递到我们选定的节点上。
javascript 复制代码
<template>
    <div>
    // 这里我们希望传递过来的属性作用在el-button上,而不是div上
        <el-button v-bind="$attrs">
            <slot></slot>
        </el-button>
    </div>
</template>

<script setup>
// 关键一步:关闭默认的自动透传 
defineOptions({ inheritAttrs: false })
</script>

$attrs是一个对象,包含了父组件传递过来,但当前组件没有声明为props的属性和事件 ,注意这里包含事件。

还是举个例子吧,父组件:

javascript 复制代码
<template>
    <MyButton size="large" @focus="handleFocus" @input="handleInput">
        点击我 
    </MyButton>
</template>

<script setup>
const handleFocus = () => {
    clg(...)
    }
</script>

四、方法暴露(defineExpose)

上面提到的都是父组件传递给子组件属性,子组件触发emit激活父组件。那么当父组件想要直接调用子组件的内部方法时:

javascript 复制代码
// 子组件 my-input.vue
<script setup>
const inputRef = ref(null)
const focus = () => {
    inputRef.value.focus()
}

defineExpose({
    focus,
    ...
})

<template>
    <input ref="inputRef" />
</template>

父组件调用

javascript 复制代码
    <template>
        <MyInput ref="myInputRef" /> 
        <button @click="myInputRef.focus()">点击聚焦</button>
    </template>

结语

这些就是简单的组件封装玩法,除此之外还有插槽的一些用法、以及逻辑抽离composable的玩法、深层嵌套组件provide/inject的用法。有兴趣的可以自己学习一下。文章有错欢迎指正!鄙人保持学习的初心。

相关推荐
回眸&啤酒鸭44 分钟前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
用户69371750013841 小时前
2026,程序员的时代拐点到了
android·前端·后端
大龄秃头程序员1 小时前
一次 iBeacon + BLE 无感解锁方案的实现记录
前端
小兔子1 小时前
Python 的 GIL 与 free-threading:3.13 之后「去 GIL」走到哪一步了
前端
IT_陈寒1 小时前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端
guslegend2 小时前
脚手架原理与本地调试:从 bin 软链接到 npm link
前端·npm·node.js·脚手架·前端工程化
海码事务所3 小时前
Google Play 新个人开发者账号上架指南:12 人连续 14 天封闭测试怎么做?
前端
田威AI3 小时前
图片内文字翻译的规格:输入输出、保真、自动化、时间与费用
前端·计算机视觉
不可能片场3 小时前
命令行中文变问号 我用环境变量救了场
前端·electron
lerhxx3 小时前
AI 应用中的上下文管理 —— 从"上下文窗口"到"分层压缩"的工程实践
前端·javascript