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

前言

最近给 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 组件本身去控制,其他地方需要,可以通过插槽去接收,而第一种就显得代码会不凝练,假如我有需要去修改选中这块逻辑,那我两个地方都要改,而且要做出一样的改动,维护的成本就无形之中增加了。

结尾

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

相关推荐
GIS程序媛—椰子15 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00121 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端24 分钟前
Content Security Policy (CSP)
前端·javascript·面试
木舟100928 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤439138 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安1 小时前
前端第二次作业
前端·css·css3
啦啦右一1 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习
半开半落1 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt