Radio 组件设计
概述
Radio 组件是本组件库中用于单项选择的表单组件。它延续了我们组件库一贯的设计理念,采用"关注点分离"和"逻辑抽象"的原则,通过 useInputState、useInputClasses等核心 Hooks 实现了强大而灵活的单选框控制功能。
Radio 组件专注于处理用户的单项选择操作,为用户提供了一套完整且易用的选择解决方案,支持单个单选框、单选框组以及按钮样式的单选框等多种形态。
设计理念
分层架构
Radio 组件严格按照三层架构设计:
- 结构层 radio.vue, radio-group.vue, radio-button.vue - 负责组件的 DOM 结构和基本布局
- 逻辑层 radioComputed.ts - 通过 Hooks 处理复杂的响应式计算
- 表现层 radio.scss, radio-group.scss, radio-button.scss - 定义组件的视觉呈现和样式规则
逻辑抽象
Radio 组件将复杂的逻辑抽象为多个核心 Hooks:
- useInputState - 处理输入状态计算
- useInputClasses - 处理类名生成
这种设计使得组件本身只需要声明所需逻辑,而具体的计算由 Hooks 完成。
核心功能设计
组件体系结构
Radio 组件采用模块化设计,包含以下三个核心组件:
- MYRadio - 基础单选框组件
- MYRadioGroup - 单选框组容器组件
- MYRadioButton - 按钮样式的单选框组件
Props 结构化设计
Radio 组件的 props 按照功能划分为多个逻辑组:
typescript
// 基础单选框属性
export const radioProps = {
modelValue: {
type: [String, Number] as PropType<string | number>,
default: ''
},
value: {
type: [String, Number] as PropType<string | number>,
default: ''
},
name: {
type: String as PropType<string>,
default: ''
},
size: {
type: String as PropType<string>,
default: ''
},
disabled: {
type: Boolean as PropType<boolean>,
default: false
}
} as const;typescript
// 单选框组属性
export const radioGroupProps = {
modelValue: {
type: [String, Number, Boolean] as PropType<string | number | boolean>,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
direction: {
type: String as PropType<'horizontal' | 'vertical'>,
default: 'horizontal',
},
gap: {
type: String,
default: '12px',
},
} as const依赖注入机制
Radio 组件通过 Vue 的依赖注入机制实现父子组件通信:
typescript
// 提供上下文
provide('radioGroup', {
modelValue: computed(() => props.modelValue),
disabled: computed(() => props.disabled),
change: updateValue
})
// 注入上下文
const radioGroup = inject<RadioGroupContext | null>('radioGroup', null)这种设计使得单选框组件能够与其父级组组件无缝协作。
样式系统设计
基础单选框样式
基础单选框采用经典的设计:
scss
.my-radio {
display: inline-flex;
align-items: center;
cursor: pointer;
&__inner {
width: 14px;
height: 14px;
border: 1px solid #dcdfe6;
border-radius: 50%;
background: #fff;
position: relative;
&:after {
content: "";
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
background: #fff;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) scale(0);
transition: transform 0.15s ease-in;
}
}
&--checked {
.my-radio__inner {
border-color: #409eff;
background-color: #409eff;
&:after {
transform: translate(-50%, -50%) scale(1);
}
}
}
}按钮样式单选框
按钮样式单选框提供现代化的外观:
scss
.my-radio-button {
position: relative;
display: inline-block;
box-sizing: border-box;
height: 36px;
line-height: 36px;
font-size: 14px;
font-weight: 500;
border: 1px solid #dcdfe6;
background: #fff;
color: #606266;
cursor: pointer;
transition: all 0.2s;
margin: 0;
padding: 0 16px;
border-radius: 0;
outline: none;
}布局控制
通过单选框组控制布局方向:
- 水平布局 - 默认布局方式
- 垂直布局 - 通过
direction="vertical"实现
组件结构设计
整体结构
Radio 组件的整体结构如下:
html
<!-- 基础单选框 -->
<label class="my-radio">
<span class="my-radio__input">
<input class="my-radio__original" type="radio" />
<span class="my-radio__inner"></span>
</span>
<span class="my-radio__label">
<slot></slot>
</span>
</label>
<!-- 单选框组 -->
<div class="my-radio-group">
<slot></slot>
</div>按钮样式结构
按钮样式单选框具有特殊的结构:
html
<label class="my-radio-button">
<input class="my-radio-button__original" type="radio" />
<span class="my-radio-button__inner">
<slot></slot>
</span>
</label>交互设计
选中状态切换
Radio 组件支持点击切换选中状态:
typescript
function handleClick() {
if (isDisabled.value) return
if (isGroup.value) {
radioGroup!.change?.(props.value)
} else {
emit('update:modelValue', props.value)
emit('change', props.value)
}
}禁用状态
Radio 组件支持禁用状态:
- 组件级别禁用 - 通过 disabled 属性控制
- 组级别禁用 - 通过 MYRadioGroup 的 disabled属性控制
键盘导航
Radio 组件支持键盘导航:
- Tab 键 - 在组件间切换焦点
- 空格键 - 选中当前聚焦的单选框
扩展性设计
Radio 组件具备良好的扩展性:
- 多种展现形式:支持基础单选框、按钮样式单选框
- 灵活布局控制:支持水平和垂直布局
- 尺寸控制:支持多种尺寸选项
- 状态控制:支持禁用状态
- 主题适配:通过 CSS 变量支持主题定制
- 组合使用:可与表单组件无缝集成
样式细节
Radio 组件在样式处理上有以下特点:
- 标准化设计:采用业界标准的单选框样式
- 动画效果:选中状态切换时具有平滑的动画效果
- 响应式设计:支持灵活的布局控制
- 无障碍设计:支持键盘导航和屏幕阅读器
- 一致性:与组件库其他表单组件保持样式一致性
性能优化
通过以下方式优化性能:
- 使用 computed 属性缓存计算结果
- 将复杂逻辑移至独立的 hooks 中
- 避免不必要的重新渲染
- 使用 Vue 3 Composition API 提高运行效率
- 合理使用依赖注入减少组件间传递
设计价值
Radio 组件延续了我们组件库的设计哲学:
- 关注点分离:结构、样式、逻辑完全解耦
- 高度复用:通过 hooks 抽象通用逻辑
- 易于维护:清晰的代码结构和命名规范
- 可扩展性强:支持丰富的定制选项
- 一致性:与 Input、Form 等其他组件保持一致的设计语言