WSの小屋

我们都知道,前端开发时使用组件库可以大大提升我们的开发效率,不用重复造轮子。

但各大组件库间风格样式不尽相同,且常常与我们真实需要的样式不一

以element-plus为例,在实际使用中我们常常要复写其样式

方案一:全局样式导入

全局创建一份样式文件,在样式文件上定义覆写的class,将该样式文件在全局导入,在需要覆盖样式的场景将class加上即可:

less 复制代码
// 重写样式,在使用时将class指定上
.el-input--normal {
  // xxx

  // 子样式,应对不同场景
  &.dark {
    // xxx
  }

  &.width-auto {
    // xxx
  }
}
// 大瓶界面内的输入框
.el-input--dashboard {
  // xxx
}
// 或直接暴力重写(直接.el-dialog,影响所有的el-dialog样式)
.el-dialog {
  // xxx
}

缺点:

  • 有时候记不住class名,要反复查看该文件,尤其是还带有子class时
  • 样式是个性化了,但使用组件时除去要手动指定class外,还需要根据情况指定props属性。例如我希望el-input默认clearable为true,这是使用class做不到的

方案二:自定义组件 包装 组件库组件

为了让el-input默认clearable为true而使用,如果组件库没有提供其全局个性化配置,我们可以自己写个组件将其封装一层,指定好默认属性和样式覆盖:

vue 复制代码
<script setup lang="ts">
import type { InputProps } from "element-plus";
import { ElInput } from "element-plus";

defineProps<InputProps>();
</script>

<template>
  <el-input class="my-input" :="$props" clearable />
</template>

<style scoped>
.el-input.my-input {
  /* 在此覆盖样式 */
}
</style>

缺点:

  • 每个组件都要自己写一个组件去封装,名字也不能相同
  • 样式和属性是可以个性化了,但对于事件只能一个一个手写emit和@xxx去继承,劝退!

方案三:一个自定义组件包装

于是,最终解决方案出现了,我们只需要封装一个自定义组件,将所有用到的默认属性class写好,通过自定义组件传入的参数不同,应用不同的默认属性与class,全自动!且带有类型提示!

vue 复制代码
<script setup lang="ts" generic="T extends 'el-input--dashboard' | 'el-dialog--normal'">
import type { DialogProps } from "element-plus";
import type { HTMLAttributes, ReservedProps } from "vue";

type Subtype = {
  "el-dialog--normal":
    | "body-p-0"
    | "width-l"
    | "width-m"
    | "width-s"
    | "width-xs"
    | "height-l"
    | "height-m"
    | "height-s"
    | "height-xs";
  "el-input--dashboard": "";

}[T];

const { type, subtype = [] } = defineProps<{
  type: T;
  subtype?: Subtype | Subtype[];
}>();

// 随着type的增多,该文件会越来越大,建议抽离出去保存到任意.ts中,再import进来
const map = {
  "el-dialog--normal": {
      draggable: true,
      alignCenter: true,
      closeOnClickModal: false,
      destroyOnClose: true,
    } as Partial<DialogProps> & HTMLAttributes & ReservedProps,
  "el-input--dashboard: {
    clearable: true,
  },
  
}
const data = computed<Record<string, any>>(() => map[type] || {});
</script>

<template>
  <slot :class="[type, ...(Array.isArray(subtype) ? subtype : [subtype])]" :="data" />
</template>

<style lang="less">
.el-dialog--normal.el-dialog {
  --el-dialog-padding-primary: 0;

  &.body-p-0 {
    .el-dialog__body {
      padding: 0;
    }
  }
}

.el-input--dashboard {
  background-color: red;
}
</style>

使用时该组件作为父组件,指定好type和subtype后,通过默认插槽取出默认属性,将其附加给目标组件,即可使用:

vue 复制代码
<wrapable type="el-dialog--normal" subtype="width-xs" #="attrs">
  <el-dialog v-model="show" :="attrs" title="Hello">
    <h1>Hello World !</h1>
  </el-dialog>
</wrapable>

缺点:

  • 想要爽用类型,前提是你要多花时间去定义它,唯一的缺点就是繁琐的前期定义

总结

以上是我在工作中,对个性化组件问题的三种解决方案,希望能够帮到你。

Comments | 0条评论