最近做开源的一些思考和收获

前言

最近给 varlet 组件库的 checkbox group 组件添加了一些新的feature,如optionslabel-keyvalue-key,这里记录下我的思考和收获。

支持 options

支持 options 的缘由

这个最开始考虑支持是因为这个 issue

耗子哥 商量了下,准备开始先支持 options,再去做这个组件

支持 options 的好处

简化代码结构

不用手动重复定义每个复选框的代码,只需在 options 属性中简单地传递复选框列表

动态生成

通过编程方式可以方便地动态生成或变更复选框选项,例如从后端获取数据后直接设置成 options

统一管理和配置

所有选项可以集中在一个数据结构中维护,方便在整个组件中统一对复选框选项数据的管理和使用,比如开关选项排布,值有所变更等均可写入该结构的逻辑内。

实现考量

label

这个字段在设计的时候,考虑支持三种类型,分别是 stringVNodeFunction,如下

ts 复制代码
export type CheckboxGroupOptionLabelRender = (option: CheckboxGroupOption, checked: boolean) => VNodeChild

export interface CheckboxGroupOption {
  label?: string | VNode | CheckboxGroupOptionLabelRender
  value?: any
  disabled?: boolean

  [key: PropertyKey]: any
}

最开始,我写这块内容的时候,考虑支持的是 label 插槽,类似于

后面和耗子哥交流了下,找到了另外一个思路,目前收集组件这种实现方式就很类似于插槽,开发者如果想使用 VNode 的话是行不通的,比如我想拿 h 函数来展示标签,模板是不支持的,就确定要支持VNode,考虑借助 tsx 的能力来实现。

支持 label-key 和 value-key

为什么要支持 label-key 和 value-key

为什么要支持 label-keyvalue-key 呢?这个我思考了下,感觉有以下几个方面

通用性与配置性

label-key 允许指定数据源中哪个属性应该显示给用户。数据源(通常是一个数据对象数组)上的属性名字可以不固定,通过配置 label-key,组件变得更加通用和可重用。例如,不同的应用场景可能有不同的数据接口

js 复制代码
const options1 = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' } ]; 

const options2 = [ 
  { id: 'a', transportation: 'Car' }, 
  { id: 'b', transportation: 'Bike' } 
 ]; 
html 复制代码
<CheckboxGroup options={options1} labelKey="name" /> 

<CheckboxGroup options={options2} labelKey="transportation" /> 

国际化

在国际化或者多语言支持的场景下,不同语言可能会有不同的展示文案。label-key 可以灵活地允许开发者动态更改显示语言

js 复制代码
const options = [ 
  { id: 1, enName: 'Apple', esName: 'Manzana' }, 
  { id: 2, enName: 'Orange', esName: 'Naranja' } 
]; 
html 复制代码
<CheckboxGroup options={options} labelKey={isSpanish ? 'esName' : 'enName'} />

自定义渲染

在某些复杂场景下,数据展示不一定是单一属性。例如

js 复制代码
const options = [ 
  { id: 1, firstName: 'Alice', lastName: 'Smith' }, 
  { id: 2, firstName: 'Bob', lastName: 'Johnson' } 
];
html 复制代码
<CheckboxGroup 
  options={options} 
  labelKey={(option) => `${option.firstName} ${option.lastName}`} 
/> 

代码

jsx 复制代码
import { defineComponent, type PropType } from 'vue'
import { isFunction } from '@varlet/shared'
import { CheckboxGroupOption } from './props'
import { createNamespace } from '../utils/components'
import Checkbox from '../checkbox'

const { name } = createNamespace('checkbox-group-option')

export default defineComponent({
  name,
  props: {
    labelKey: {
      type: String,
      required: true,
    },
    valueKey: {
      type: String,
      required: true,
    },
    option: {
      type: Object as PropType<CheckboxGroupOption>,
      required: true,
    },
  },
  setup(props) {
    return () => {
      const { option, labelKey, valueKey } = props

      return (
        <Checkbox checkedValue={option[valueKey]} disabled={option.disabled}>
          {{
            default: ({ checked }: { checked: boolean }) =>
              isFunction(option[labelKey]) ? option[labelKey](option, checked) : option[labelKey],
          }}
        </Checkbox>
      )
    }
  },
})

高内聚概念的实践

相信很多掘友都听说过 高内聚低耦合 这个软件工程的概念,我刚开始参加工作的时候,也知道这个概念,但是自己在真正的编码过程中,很少意识到自己可以主动践行这个概念。

前两天早上起床的时候,我打开微信,看到了耗子哥给我的留言,

一下子就想到了 高内聚 这个概念,对于 checkbox 组件,是否选中这个状态,是由它本身产生并控制的;而对于 checkbox group 组件,渲染出来的每一个 checkbox 组件,都能获取到组件是否被选中了。

但是,这里有两种思路去实现。

checkbox group 组件自己处理选中状态

checkbox group 组件这里加个判断,当前这个 optionvalue 是否在 modelValue 这个数组里面,对应我pr里面的code就是

checkbox 组件通过插槽暴露选中状态

checkbox 组件有几个插槽,如下图

其中,三个是 icon 相关的,肯定不合适,因为这三个插槽和选中状态没有深的关联,一般作为ui展示部分。

对于默认插槽,一般会渲染选项的标签,是比较适合来传递选中状态的,对应pr的code是

第二种选择就对应了 高内聚 概念,选中这个状态只有 checkbox 组件本身去控制,其他地方需要,可以通过插槽去接收,而第一种就显得代码会不凝练,假如我有需要去修改选中这块逻辑,那我两个地方都要改,而且要做出一样的改动,维护的成本就无形之中增加了。

结尾

以上就是我要分享的全部内容了,如有错误,欢迎指正~

相关推荐
恋猫de小郭11 分钟前
Redis 作者反驳「中国模型之所以强,是因为通过 API 蒸馏了美国模型」
前端·人工智能·ai编程
Darling噜啦啦15 分钟前
Canvas 游戏开发与数据可视化实战:从飞机大战到 ECharts 报表
前端·echarts·canvas
Esaka_Forever18 分钟前
codex和open claude两者只有客户端工具开源,底层大模型权重全部闭源
开源
OpenTiny社区27 分钟前
这次更新太良心!GenUI SDK v1.2.0 轻量化 + 稳流式 + 超强 Playground
前端·vue.js·ai编程
梨子同志27 分钟前
WebGL test
前端
程序员黑豆29 分钟前
AI全栈开发系列开篇:从Java全栈到AI应用实战
前端·ai编程·全栈
yangyj32 分钟前
从 PDR 到落地:用 Codex 完成一次 Rspack 升级
前端
程序员鱼皮32 分钟前
提示词工程已死,Loop Engineering 称王!保姆级教程 + 项目实战
前端·后端·ai编程
太阳之子36 分钟前
用嘴做设计?这个 Claude Code Skill 让我的 Figma 吃灰了
开源
小爷毛毛_卓寿杰1 小时前
给 Embedding 模型也加一块“游乐场“—— Xinference 是怎么把 vector 变成肉眼可见的体验的
前端