如何进行组件封装

如何进行Vue组件封装与优化

在前端开发中,组件化是一种常见的设计模式,它可以帮助我们更好地组织和复用代码。本文将详细介绍如何通过合理使用输入(props)、输出(emits)、双向绑定、属性透传以及方法暴露等技术来实现高效的Vue组件封装。

一、输入(Props)与输出(Emits)

定义通用按钮组件

需求:

  • 支持多种类型,如primary、danger和default
  • 禁用状态控制
  • 触发点击事件

<template>
    <button class="base-btn"
        :class="[`base-btn--${type}`]"
        :disabled="disabled" 
        @click="handleClick">

<slot></slot>    
    </button>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
    type: {
        type: String,
        default: 'default',
        validator(value) {
            return ['primary', 'danger', 'default'].includes(value)
        }
    },
    disabled: Boolean
})

const emit = defineEmits(['click'])

function 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>

父组件使用示例


<template>

<div>
        <!-- 使用primary类型 -->
        <BaseButton type="primary" @click="saveData">保存</BaseButton> 
        <!-- 使用danger类型,禁用 -->
        <BaseButton type="danger" :disabled="true">保存</BaseButton>
    </div>
</template>

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

## 二、双向绑定(v-model)

### 实现通用输入框组件

**需求:**

- 使用`v-model`进行双向绑定
- 支持基本的数据类型,如字符串和数字

```vue
&amp;lt;template&amp;gt;
    &amp;lt;input :value="modelValue" @input="handleInput"&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
    modelValue: {
        type: [String, Number],
        default: ''
    }
})

const emit = defineEmits(['update:modelValue'])

function handleInput(event) {
    emit('update:modelValue', event.target.value)
}
&amp;lt;/script&amp;gt;

三、属性透传

自动与手动透传

需求:

  • 允许父组件传递未声明的属性给子组件
  • 手动控制这些属性的分配位置

在Vue 3中,当一个组件只有一个根元素时,父组件中的未知属性会被自动添加到该根元素上。例如:

&amp;lt;template&amp;gt;
    &amp;lt;el-button&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/el-button&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;
// 不声明size、loading等属性
&amp;lt;/script&amp;gt;

使用场景: 在某些情况中,可能需要手动控制这些属性的分配方式。

手动透传示例

&amp;lt;template&amp;gt;
    &amp;lt;div v-bind="$attrs"&amp;gt;
        &amp;lt;el-button v-bind="$attrs"&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/el-button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;
import { defineOptions } from 'vue'
defineOptions({ inheritAttrs: false })
&amp;lt;/script&amp;gt;

这里 $attrs 包含了所有未声明为 props 的属性和事件。

四、方法暴露(defineExpose)

需求:

  • 让父组件能够直接访问子组件的方法
&amp;lt;template&amp;gt;
    &amp;lt;input ref="inputRef"&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;
import { ref } from 'vue'

const inputRef = ref(null)

function focus() {
    inputRef.value.focus()
}

defineExpose({
    focus,
})
&amp;lt;/script&amp;gt;

父级使用:


&amp;lt;template&amp;gt;
    &amp;lt;MyInput ref="myInputRef" /&amp;gt; 
    &amp;lt;button @click="myInputRef.focus()"&amp;gt;点击聚焦&amp;lt;/button&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;
import MyInput from './components/MyInput.vue'
&amp;lt;/script&amp;gt;
``

## 总结

本文介绍了一些常用的组件封装技术,包括如何通过 props 和 emits` 实现父子组件间的通信、双向绑定的实现方式以及如何透传属性。这些技巧可以帮助我们构建出更健壮且易维护的应用程序。

建议在实际项目中结合使用插槽、逻辑抽离(Composable)和深层嵌套场景下的提供/注入(Provide/Inject)等技术,以进一步提升组件的灵活度与可复用性。