Radio 单选框组件完全指南:从基础用法到源码实现)
ElementVue 2.0Radio 单选框组件完全指南从基础用法到源码实现【免费下载链接】elementA Vue.js 2.0 UI Toolkit for Web项目地址: https://gitcode.com/gh_mirrors/eleme/elementElement 的 Radio 组件族el-radio、el-radio-group、el-radio-button是 Vue 2.0 表单体系中用于互斥单选场景的标准方案。本文以官方文档 examples/docs/fr-FR/radio.md 为骨架完整覆盖基础用法、禁用、单选组、按钮样式、带边框形态及全部属性/事件 API并深入组件源码与单元测试剖析v-model数据流、尺寸解析链、键盘导航与无障碍设计等底层实现帮助你既会用、又懂其原理。一、组件概览与适用场景Radio 用于在一组互斥的备选项中单选一项。Element 将其拆分为三个可组合的组件组件角色源码位置el-radio单个单选框radio.vueel-radio-group单选组容器统一管理选中值radio-group.vueel-radio-button按钮形态的单选框必须置于el-radio-group内radio-button.vue官方文档给出的使用建议是Radio 的选项默认全部可见因此选项不宜过多若选项过多应改用 Select 选择器。这一设计取舍来源于原生 radio 的交互模型——所有候选值同时呈现给用户方便直接对比和快速点选适合 27 个固定选项的场景而 Select 将选项收进下拉面板适合大量选项或空间受限的场景。使用前先注册组件。单个组件可通过 packages/radio/index.js 安装Radio.install function(Vue) { Vue.component(Radio.name, Radio); }项目级引入则由根目录 components.json 统一注册也可在入口处按需引入import Vue from vue; import { Radio, RadioGroup, RadioButton } from element-ui; Vue.use(Radio); Vue.use(RadioGroup); Vue.use(RadioButton);二、基础用法v-model 与 label 的绑定语义官方文档指出使用 Radio 组件只需将变量绑定到v-model选中的含义是变量的值等于对应 Radiolabel属性的值label可以是String、Number或Boolean。template el-radio v-modelradio label1Option A/el-radio el-radio v-modelradio label2Option B/el-radio /template script export default { data () { return { radio: 1 }; } } /script这段代码中两个el-radio共享同一个radio变量点击任一选项即把该选项的label值写入radio。关键点在于label的三态类型String如上例、Number、Boolean均可作为选中值。注意当 label 是数字或布尔值时必须使用:label动态绑定而非字符串字面量否则 Vue 会把它当成字符串el-radio v-modelradio :label3Option A/el-radio el-radio v-modelradio :labelfalseOption B/el-radio从源码看这一双向绑定由model计算属性承担radio.vuemodel: { get() { return this.isGroup ? this._radioGroup.value : this.value; }, set(val) { if (this.isGroup) { this.dispatch(ElRadioGroup, input, [val]); } else { this.$emit(input, val); } this.$refs.radio (this.$refs.radio.checked this.model this.label); } }当el-radio单独使用时不在组内model的 getter 直接返回自身valueprop即外部v-model绑定的值setter 通过$emit(input, val)把新选中值回写给父组件完成单向数据流闭环选中态的判定则是严格相等比较model label。另外模板中label属性还承担了默认文案职责当标签内没有插槽内容时radio.vue 会直接把label值渲染为文本template v-if!$slots.default{{label}}/template所以el-radio label1Option A/el-radio与省略文案只写label的写法都合法。三、禁用状态disabled 的三种来源只需在el-radio上添加disabled属性即可禁用true为禁用。官方文档示例同时演示了普通禁用与选中且禁用两种状态template el-radio disabled v-modelradio labeldisabledOption A/el-radio el-radio disabled v-modelradio labelselected and disabledOption B/el-radio /template script export default { data () { return { radio: selected and disabled }; } } /scriptdisabled接受Boolean默认值为false。源码中的isDisabled计算属性radio.vue揭示了一个重要事实——禁用状态存在三级传递链isDisabled() { return this.isGroup ? this._radioGroup.disabled || this.disabled || (this.elForm || {}).disabled : this.disabled || (this.elForm || {}).disabled; }即单个 Radio 的禁用条件为el-radio-group的disabled若在组内|| 自身disabled|| 外层el-form的disabled任意一级为真即禁用。这意味着在el-radio-group上设置disabled可一键禁用组内所有选项在el-form上设置disabled可禁用整个表单内的所有 Radio单个el-radio自身的disabled优先级最高可精确控制某一个选项。禁用后组件会挂上is-disabledclass模板中{ is-disabled: isDisabled }并把原生的disabled属性透传到隐藏的input typeradio上同时更新aria-disabled无障碍状态。单元测试 radio.spec.js 中验证了禁用后点击不会改变值radioElm.click()之后vm.radio仍为空串。四、单选组 el-radio-group互斥管理、change 事件与键盘导航当多个互斥选项需要作为一个整体管理时使用el-radio-group包裹el-radio只需在el-radio-group上绑定v-model并在每个el-radio上设置label无需再给每个子项单独绑定变量。组还提供了change事件回调参数为当前选中的值。template el-radio-group v-modelradio el-radio :label3Option A/el-radio el-radio :label6Option B/el-radio el-radio :label9Option C/el-radio /el-radio-group /template script export default { data () { return { radio: 3 }; } } /script4.1 组内数据流基于 emitter 的 dispatch 机制组的核心价值在于把值存储从各子项上收拢到组容器。回顾radio.vue的isGroup计算属性radio.vue它会沿$parent链向上查找componentName ElRadioGroup的祖先一旦找到子项的modelgetter 就改为读取this._radioGroup.valuesetter 则改为调用this.dispatch(ElRadioGroup, input, [val])。dispatch来自 src/mixins/emitter.js它沿父级链向上找到指定componentName的组件并对其$emitdispatch(componentName, eventName, params) { var parent this.$parent || this.$root; var name parent.$options.componentName; while (parent (!name || name ! componentName)) { parent parent.$parent; ... } if (parent) { parent.$emit.apply(parent, [eventName].concat(params)); } }子项 dispatch 出的input事件会更新el-radio-group的valueprop从而触发外部v-model同步。与此同时子项在handleChange中还会dispatch(ElRadioGroup, handleChange, this.model)而组在created钩子中监听handleChange并转发为对外暴露的change事件radio-group.vuecreated() { this.$on(handleChange, value { this.$emit(change, value); }); }因此el-radio-group的change事件只在用户交互点击或键盘选中时触发程序化地修改v-model不会触发——这一点由测试用例 change event only triggers on user input 专门断言vm.radio 6后回调数据保持不变。4.2 表单联动组还监听了自身value的变化radio-group.vuewatch: { value(value) { this.dispatch(ElFormItem, el.form.change, [this.value]); } }选中值变化时组会向最近的ElFormItem广播el.form.change事件驱动表单校验与重新校验这是 Radio 无缝融入el-form验证体系的关键链路。4.3 键盘方向键导航组件内置的可用性细节el-radio-group内置了完整的键盘操作支持[radio-group.vue](https://link.gitcode.com/i/c5c8f9a34c7b090a61ecce6b3620ac49#L14-L19, L69-L104)监听keydown当用户按下左37/上38/右39/下40方向键时在组内选项间循环切换焦点与选中态到达首尾时回绕第一个按左跳到最后一个最后一个按右跳回第一个。mounted钩子还会做一次兜底当组内没有任何选项被选中时把第一个[roleradio]元素的tabIndex设为 0保证键盘用户能通过 Tab 进入单选组。测试 keyboard event 用triggerKeyDown依次模拟了方向键与回车键13验证了选中值在 3 → 6 → 9 之间的循环迁移。五、按钮样式 el-radio-buttonsize 与 fill/text-color 定制将el-radio换成el-radio-button即可得到按钮形态的单选组合el-radio-button同样需要放在el-radio-group内。size属性用于调整尺寸。官方文档给出了四种组合示例默认、medium、small 单项禁用、mini 整组禁用template div el-radio-group v-modelradio1 el-radio-button labelNew York/el-radio-button el-radio-button labelWashington/el-radio-button el-radio-button labelLos Angeles/el-radio-button el-radio-button labelChicago/el-radio-button /el-radio-group /div div stylemargin-top: 20px el-radio-group v-modelradio2 sizemedium el-radio-button labelNew York /el-radio-button el-radio-button labelWashington/el-radio-button el-radio-button labelLos Angeles/el-radio-button el-radio-button labelChicago/el-radio-button /el-radio-group /div div stylemargin-top: 20px el-radio-group v-modelradio3 sizesmall el-radio-button labelNew York/el-radio-button el-radio-button labelWashington disabled /el-radio-button el-radio-button labelLos Angeles/el-radio-button el-radio-button labelChicago/el-radio-button /el-radio-group /div div stylemargin-top: 20px el-radio-group v-modelradio4 disabled sizemini el-radio-button labelNew York/el-radio-button el-radio-button labelWashington/el-radio-button el-radio-button labelLos Angeles/el-radio-button el-radio-button labelChicago/el-radio-button /el-radio-group /div /template script export default { data () { return { radio1: New York, radio2: New York, radio3: New York, radio4: New York }; } } /script源码层面el-radio-button与el-radio的最大差异在于选中态的呈现方式。它的value计算属性直接读写_radioGroup.valueradio-button.vue而激活时的配色由activeStyle计算属性驱动radio-button.vueactiveStyle() { return { backgroundColor: this._radioGroup.fill || , borderColor: this._radioGroup.fill || , boxShadow: this._radioGroup.fill ? -1px 0 0 0 ${this._radioGroup.fill} : , color: this._radioGroup.textColor || }; }这对应了el-radio-group上的两个定制属性fill按钮激活时的填充色与边框色默认#409EFFElement 主色text-color按钮激活时的文本颜色默认#ffffff。测试用例 custom color 直接断言了fill#000 text-color#ff0时activeStyle中backgroundColor、borderColor、color的取值与上述实现完全对应。注意size的取值按官方文档size接受medium / small / mini而 types/radio-group.d.ts 中类型定义为RadioGroupSize large | small测试用例则使用了sizelarge对应el-radio-button--largeclass。实际项目中请以当前版本组件文档的medium / small / mini为准同时留意类型声明的差异。尺寸最终会渲染为el-radio-button--{size}前缀 class由 radio.scss 中的对应规则控制内边距、圆角与字号。六、带边框形态border 属性设置border属性可将单选框渲染为带边框的卡片形态size只有在border为真时才生效。官方文档示例覆盖了四种组合template div el-radio v-modelradio1 label1 borderOption A/el-radio el-radio v-modelradio1 label2 borderOption B/el-radio /div div stylemargin-top: 20px el-radio v-modelradio2 label1 border sizemediumOption A/el-radio el-radio v-modelradio2 label2 border sizemediumOption B/el-radio /div div stylemargin-top: 20px el-radio-group v-modelradio3 sizesmall el-radio label1 borderOption A/el-radio el-radio label2 border disabledOption B/el-radio /el-radio-group /div div stylemargin-top: 20px el-radio-group v-modelradio4 sizemini disabled el-radio label1 borderOption A/el-radio el-radio label2 borderOption B/el-radio /el-radio-group /div /template script export default { data () { return { radio1: 1, radio2: 1, radio3: 1, radio4: 1 }; } } /script对应到源码与样式border为真时根元素挂上is-borderedclassradio.vue并依据radioSize附加el-radio--{size}尺寸 classradio.vueradio.scss 中的when(bordered)规则为带边框形态设置内边距、圆角、边框与固定高度选中态is-checked时边框变为主色禁用态is-disabled变为浅色并显示not-allowed光标尺寸解析链在radioSize计算属性中radio.vue优先取自身size其次取外层el-form-item的elFormItemSize再回退到全局$ELEMENT.size若在组内组的radioGroupSize优先级最高。这就是组上设 size、整个组统一切换尺寸的实现基础。七、属性与事件 API 全表以下完整继承官方文档examples/docs/fr-FR/radio.md的参数表格。7.1 Radio 属性属性说明类型可选值默认值value / v-model绑定值string / number / boolean——labelRadio 的 valuestring / number / boolean——disabled是否禁用boolean—falseborder是否显示边框boolean—falsesizeRadio 的尺寸仅在border为真时有效stringmedium / small / mini—name原生 name 属性string——7.2 Radio 事件事件名说明回调参数change绑定值变化时触发仅在用户交互时选中的 Radio label 值补充说明源码中modelsetter 还会在单独使用时$emit(input, val)支撑v-model语法与change事件是两条独立通道——input负责同步数据change负责对外通知用户操作。7.3 Radio-group 属性属性说明类型可选值默认值value / v-model绑定值string / number / boolean——size单选框组尺寸仅对按钮形式的 Radio 或带边框的 Radio 有效stringmedium / small / mini—disabled是否禁用整组boolean—falsetext-color按钮形式的 Radio 激活时的文本颜色string—#fffffffill按钮形式的 Radio 激活时的填充色和边框色string—#409EFF7.4 Radio-group 事件事件名说明回调参数change绑定值变化时触发仅在用户交互时选中的 Radio label 值7.5 Radio-button 属性属性说明类型可选值默认值labelRadio 的 valuestring / number——disabled是否禁用boolean—falsename原生 name 属性string——对应的 TypeScript 类型声明可查阅 types/radio.d.ts、types/radio-group.d.ts 与 types/radio-button.d.ts供类型检查与 IDE 智能提示使用。八、源码级原理数据流、可访问性与尺寸解析8.1 组件间通信模型Radio 三件套没有使用 Vuex 或 props 逐层透传而是采用 Element 自研的emitter 广播机制src/mixins/emitter.js。子组件通过componentNameElRadioGroup向上查找最近的组容器再用$emit触发input/handleChange事件组容器收到后更新value、转发change并向ElFormItem广播el.form.change驱动校验。这种按名称寻亲的解耦方式使得el-radio即使被其他组件如布局、循环包裹也能正确归组。8.2 无障碍与键盘交互el-radio根元素声明roleradio实时同步aria-checkedmodel label与aria-disabled原生input typeradio被隐藏opacity: 0且z-index: -1见 radio.scss但保留其表单语义焦点管理tabIndex计算属性radio.vue让未选中的组内选项tabIndex-1、选中项或独立项为0配合组容器的方向键循环切换实现单一 Tab 进入、方向键游走的原生 radio 体验空格键选中keydown.space.stop.preventmodel isDisabled ? model : label让聚焦的选项可用空格键选中。8.3 样式体系组件样式集中在 packages/theme-chalk/src/radio.scss全部基于 SCSS 变量如$--radio-font-color、$--radio-input-border-color-hover、$--radio-bordered-*尺寸族可通过 Element 的主题定制机制全局覆盖。选中圆点的::after伪元素通过transform: scale(0)→scale(1)实现 0.15s 的展开动画聚焦时box-shadow高亮边框。九、单元测试覆盖行为即契约test/unit/specs/radio.spec.js 共覆盖 14 个用例是理解组件行为的权威依据Radio 基础创建渲染出el-radioclass、点击后出现is-checkeddisabled点击不变值且挂is-disabledborder挂is-borderedchange 语义点击触发change且回调参数为 label 值程序化赋值不触发changeRadio、Radio-group、Radio-button 三处均断言Radio-group组内点击切换选中态与绑定值组级disabled使所有子项禁用方向键37/38/39/40与回车在组内循环切换el-radio-button作为子项时同样受组级禁用约束Radio-buttonis-active切换、fill/text-color自定义配色、sizelarge渲染el-radio-button--largeclass。这些测试与上文分析的源码实现一一对应可视为组件行为的可执行契约。十、小结本文以 examples/docs/fr-FR/radio.md 为骨架完整覆盖了el-radio、el-radio-group、el-radio-button的使用方法、全部属性/事件 API并从源码与测试两个维度展开model计算属性与 emitter 广播构成的双向绑定数据流、三级禁用传递链组 / 自身 / 表单、基于activeStyle的按钮配色定制、border形态与尺寸解析链以及内置的键盘导航与无障碍支持。掌握这些你不仅能正确选用 Radio 的三种形态应对不同 UI 场景还能在出现异常时快速定位到 radio.vue、radio-group.vue 与 radio.scss 的具体实现真正做到知其然且知其所以然。【免费下载链接】elementA Vue.js 2.0 UI Toolkit for Web项目地址: https://gitcode.com/gh_mirrors/eleme/element创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考