Element UI 组件库

组件封装与复用全攻略

详细讲解如何在 Element UI 中封装对话框、输入框和选择器组件,实现逻辑复用与样式统一。

🎯 引言

掌握组件封装与复用是前端开发中的必备技能。通过封装 Element UI 组件,不仅能减少重复代码,还能统一界面风格,提高项目的可维护性和扩展性。这篇文章将手把手教你如何封装对话框、输入框和选择器组件,让你轻松驾驭 Element UI。


🔧 为什么要封装组件?

在项目开发中,你可能会遇到很多重复使用的组件,比如弹窗、输入框、下拉选择等。每次都写一遍逻辑或者样式不仅浪费时间,还容易出错。封装组件的好处主要表现在:

  • 复用逻辑,避免重复造轮子。
  • 统一样式,保证整体 UI 风格一致。
  • 方便维护,修改一次即可影响所有实例。
  • 支持灵活的定制,比如插槽、事件回调让组件更通用。

💬 封装对话框组件

Element UI 的 el-dialog 是非常实用的弹窗组件,封装它能让你快速创建各种弹窗,且支持灵活配置。

1. 定义常用属性

我们通常需要设置弹窗的标题、宽度、底部按钮文字等。这里封装一个基础的对话框组件 MyDialog

MyDialog.vue
<template>
    <el-dialog :title="title" :visible.sync="visible" :width="width" @close="handleClose">
        <slot></slot>
        <span slot="footer" class="dialog-footer">
            <el-button @click="handleCancel">{{ cancelText }}</el-button>
            <el-button type="primary" @click="handleConfirm">{{ confirmText }}</el-button>
        </span>
    </el-dialog>
</template>

<script>
export default {
    name: 'MyDialog',
    model: {
        prop: 'value',
        event: 'update:value',
    },
    props: {
        value: {
            type: String,
            default: '',
        },
        title: {
            type: String,
            default: '提示',
        },
        width: {
            type: String,
            default: '50%',
        },
        confirmText: {
            type: String,
            default: '确定',
        },
        cancelText: {
            type: String,
            default: '取消',
        },
    },
    computed: {
        visible: {
            get() {
                return this.value;
            },
            set(val) {
                this.$emit('update:value', val);
            },
        },
    },
    methods: {
        handleConfirm() {
            this.$emit('confirm');
        },
        handleCancel() {
            this.$emit('cancel');
            this.$emit('update:value', false);
        },
        handleClose() {
            this.$emit('close');
        },
    },
};
</script>

2. 使用示例

App.vue
<template>
    <div>
        <el-button @click="showDialog = true">打开对话框</el-button>
        <my-dialog
            v-model="showDialog"
            title="自定义标题"
            width="60%"
            confirmText="确定"
            cancelText="关闭"
            @confirm="onConfirm"
            @cancel="onCancel"
        >
            <p>这是对话框内容,可以放任何内容。</p>
        </my-dialog>
    </div>
</template>

<script>
import MyDialog from './MyDialog.vue';

export default {
    components: { MyDialog },
    data() {
        return {
            showDialog: false,
        };
    },
    methods: {
        onConfirm() {
            this.$message.success('点击了确定!');
            this.showDialog = false;
        },
        onCancel() {
            this.$message.info('点击了取消!');
        },
    },
};
</script>
封装对话框时,建议尽量通过 prop 和事件暴露控制权限,避免直接操作内部状态,这样更灵活也更安全。

📝 封装输入框组件

输入框是表单中最常用的元素。Element UI 的 el-input 支持丰富的属性和事件,封装时可以加入校验、长度限制以及统一样式。

1. 基础封装

创建一个 MyInput 组件:

MyInput.vue
<template>
    <el-form-item :label="label" :prop="prop" :rules="rules">
        <el-input
            v-model="innerValue"
            :placeholder="placeholder"
            :maxlength="maxlength"
            show-word-limit
        />
    </el-form-item>
</template>

<script>
export default {
    name: 'MyInput',
    model: {
        prop: 'value',
        event: 'update:value',
    },
    props: {
        value: {
            type: String,
            default: '',
        },
        label: {
            type: String,
            default: '',
        },
        placeholder: {
            type: String,
            default: '请输入内容',
        },
        prop: {
            type: String,
            default: '',
        },
        rules: {
            type: Array,
            default: () => [],
        },
        maxlength: {
            type: Number,
            default: 50,
        },
    },
    computed: {
        innerValue: {
            get() {
                return this.value;
            },
            set(val) {
                this.$emit('update:value', val);
            },
        },
    },
};
</script>

<style scoped>
.el-input {
    width: 100%;
}
</style>

2. 使用示例

<template>
    <el-form :model="form" :rules="rules" ref="formRef">
        <my-input
            v-model="form.username"
            label="用户名"
            prop="username"
            :rules="rules.username"
            :maxlength="20"
        />
        <el-button type="primary" @click="submitForm">提交</el-button>
    </el-form>
</template>

<script>
import MyInput from './MyInput.vue';

export default {
    components: { MyInput },
    data() {
        return {
            form: {
                username: '',
            },
            rules: {
                username: [
                    {
                        required: true,
                        message: '请输入用户名',
                        trigger: 'blur',
                    },
                    {
                        min: 3,
                        max: 20,
                        message: '长度在 3 到 20 个字符',
                        trigger: 'blur',
                    },
                ],
            },
        };
    },
    methods: {
        submitForm() {
            this.$refs.formRef.validate(valid => {
                if (valid) {
                    this.$message.success('提交成功!');
                }
            });
        },
    },
};
</script>
el-form-itemprop 与规则对应,封装输入框后,配合 Element UI 的表单验证机制能大大提升开发效率。

🎯 封装选择器组件

选择器组件在业务场景中非常常见,比如下拉菜单、多选框等。Element UI 提供了丰富的选择器组件,我们这里以封装一个基础的单选下拉选择器为例。

1. 基础封装

封装一个 MySelect 组件

MySelect.vue
<template>
    <el-form-item :label="label" :prop="prop" :rules="rules">
        <el-select v-model="innerValue" :placeholder="placeholder" @change="onChange">
            <el-option
                v-for="item in options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            />
        </el-select>
    </el-form-item>
</template>

<script>
export default {
    name: 'MySelect',
    model: {
        prop: 'value',
        event: 'update:value',
    },
    props: {
        value: {
            required: true,
        },
        label: {
            type: String,
            default: '',
        },
        prop: {
            type: String,
            default: '',
        },
        rules: {
            type: Array,
            default: () => [],
        },
        placeholder: {
            type: String,
            default: '请选择',
        },
        options: {
            type: Array,
            default: () => [],
        },
    },
    computed: {
        innerValue: {
            get() {
                return this.value;
            },
            set(val) {
                this.$emit('update:value', val);
            },
        },
    },
    methods: {
        onChange(val) {
            this.$emit('input', val);
            this.$emit('change', val);
        },
    },
};
</script>

<style scoped>
.el-select {
    width: 100%;
}
</style>

2. 使用示例

App.vue
<template>
    <el-form :model="form" :rules="rules" ref="formRef">
        <my-select
            v-model="form.gender"
            label="性别"
            prop="gender"
            :options="genderOptions"
            :rules="rules.gender"
        />
        <el-button type="primary" @click="submitForm">提交</el-button>
    </el-form>
</template>

<script>
import MySelect from './MySelect.vue';

export default {
    components: { MySelect },
    data() {
        return {
            form: {
                gender: '',
            },
            genderOptions: [
                { label: '', value: 'male' },
                { label: '', value: 'female' },
            ],
            rules: {
                gender: [
                    {
                        required: true,
                        message: '请选择性别',
                        trigger: 'change',
                    },
                ],
            },
        };
    },
    methods: {
        submitForm() {
            this.$refs.formRef.validate(valid => {
                if (valid) {
                    this.$message.success('提交成功!');
                }
            });
        },
    },
};
</script>
封装选择器时,建议通过 options 传递选择项,保持组件通用性;事件 change 也要支持,方便业务调用。

🧾 小节总结

  • 组件封装能有效减少重复代码,实现样式和逻辑的统一,提高项目整体质量。
  • 对话框组件封装时要支持插槽、自定义回调,满足不同业务需求。
  • 输入框和选择器组件的封装,结合 Element UI 的表单验证规则,提高表单开发效率和体验。

❓ 知识问答(Q&A)

Q:为什么要用 .sync 修饰符绑定对话框的 visible 属性?

A:.sync 修饰符用于实现父组件和子组件之间的双向绑定。当对话框内调用 update:visible 事件时,父组件的 visible 状态也会同步更新,实现显示与隐藏的同步控制。

Q:封装输入框时,如何实现统一的校验规则?

A:可以通过 el-formel-form-itemrules 属性传入统一的验证规则,输入框组件只需将 proprules 作为参数传递,结合 Element UI 的校验机制即可。


🧪 小练习

练习1

在封装对话框基础上,添加一个自定义底部按钮,按钮文字和事件由父组件传入。

<template>
    <my-dialog :visible.sync="visible" title="多按钮对话框" @confirm="onConfirm" @cancel="onCancel">
        <p>内容区域</p>
        <template v-slot:footer>
            <!-- 自定义按钮区域 -->
        </template>
    </my-dialog>
</template>

<script>
export default {
    data() {
        return { visible: false };
    },
    methods: {
        onConfirm() {
            /* 处理确认 */
        },
        onCancel() {
            /* 处理取消 */
        },
    },
};
</script>

练习2

封装一个多选下拉组件,支持传入多选选项,返回选中的数组。

<template>
    <el-select v-model="innerValue" multiple :placeholder="placeholder" @change="onChange">
        <el-option
            v-for="item in options"
            :key="item.value"
            :label="item.label"
            :value="item.value"
        />
    </el-select>
</template>

<script>
export default {
    props: {
        /* ... */
    },
    data() {
        /* ... */
    },
    methods: {
        /* ... */
    },
};
</script>

🎉 恭喜你已经掌握 Element UI 组件封装、插槽使用、表单校验这几项技能啦!