如何进行组件封装

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

一、输入(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的用法。有兴趣的可以自己学习一下。文章有错欢迎指正!鄙人保持学习的初心。

相关推荐
難釋懷1 小时前
Redis服务器端优化-慢查询优化
前端·redis·bootstrap
sghuter2 小时前
Chrome如何重塑Web标准未来
前端·chrome
渣渣xiong2 小时前
从零开始:前端转型AI agent直到就业第十四天-第十七天
前端·人工智能
changshuaihua0012 小时前
React 入门
前端·javascript·react.js
兰为鹏2 小时前
做前端需求总结出的非常好用的skill
前端
笨笨狗吞噬者2 小时前
Opus 4.7 skill 编写和使用实践
前端·ai编程
舞影天上2 小时前
WordPress MCP Adapter 调试实战:从"连接失败"到完全可用
前端·ai编程
可视之道2 小时前
Web组态编辑器的撤销重做架构设计
前端
掘金安东尼3 小时前
本周前端与 AI 技术情报|前端下一步 #462
前端·javascript·面试