Element Plus Switch 组件完全指南:从基础用法到源码级实现原理
【免费下载链接】element-plus🎉 A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plus
Element Plus 的ElSwitch组件用于在两个对立状态之间进行切换,是表单场景中最常用的开关控件之一。本文以 docs/en-US/component/switch.md 官方文档为骨架,结合 switch.vue 源码 与 switch.ts 类型定义,系统讲解其全部用法、API 属性、事件、插槽与底层实现原理,帮助你从"会用"进阶到"懂原理"。
基础用法(Basic usage)
Switch 通过v-model绑定一个Boolean类型的变量即可完成双向绑定,这也是最常用的场景。在 basic.vue 示例 中可以看到,开关打开与关闭两个状态下的背景色由两个 CSS 变量决定:
--el-switch-on-color:开启状态下的背景色--el-switch-off-color:关闭状态下的背景色
<template> <el-switch v-model="value1" /> <el-switch v-model="value2" class="ml-2" style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949" /> </template> <script lang="ts" setup> import { ref } from 'vue' const value1 = ref(true) const value2 = ref(true) </script>从样式源码 packages/theme-chalk/src/switch.scss 可以看到,当组件处于checked状态时,核心轨道(.el-switch__core)的background-color会取getCssVar('switch-on-color'),即--el-switch-on-color;未选中时背景色则为--el-switch-off-color。这两个 CSS 变量属于主题变量体系,既可以在组件内联样式覆盖,也可以在使用主题定制时全局调整。
底层模型:受控与非受控
从源码角度理解,model-value与active-value/inactive-value的匹配关系决定了开关的显示状态。在 switch.vue 中:
const isControlled = ref(props.modelValue !== false) const actualValue = computed(() => { return isControlled.value ? props.modelValue : false }) const checked = computed(() => actualValue.value === props.activeValue)checked的计算方式是严格相等比较:只有当当前值=== activeValue时开关才处于开启状态。同时,如果初始传入的model-value既不等于active-value也不等于inactive-value,源码会通过debugWarn输出警告,并自动将值矫正为inactiveValue:
if (![props.activeValue, props.inactiveValue].includes(actualValue.value)) { debugWarn(COMPONENT_NAME, 'model-value must be active-value or inactive-value') emit(UPDATE_MODEL_EVENT, props.inactiveValue) // ... }这说明在开发中务必保证绑定的值只来源于active-value或inactive-value二者之一,否则会收到警告并被强制归位。
尺寸(Sizes)
Switch 提供了large、default(默认)、small三种尺寸,通过size属性控制,见 sizes.vue 示例:
<el-switch v-model="value" size="large" active-text="Open" inactive-text="Close" /> <br /> <el-switch v-model="value" active-text="Open" inactive-text="Close" /> <br /> <el-switch v-model="value" size="small" active-text="Open" inactive-text="Close" />尺寸的取值在 switch.ts 中通过isValidComponentSize校验器约束,只允许'' | 'large' | 'default' | 'small'。需要注意的是,Switch 的size会通过useFormSize()与所在el-form/el-form-item的size联动——当你设置了表单级尺寸而未显式指定 Switch 的size时,开关会自动继承表单的尺寸,这保证了表单内控件的一致性。
文字描述(Text description)
通过active-text与inactive-text属性可以为开关两个状态分别添加文字说明。参见 text-description.vue 示例:
<el-switch v-model="value1" active-text="Pay by month" inactive-text="Pay by year" />在默认(非inline-prompt)模式下,文字渲染在轨道两侧的独立标签区(.el-switch__label--left/--right);开启状态的文字在右侧、关闭状态的文字在左侧,并且只有当前生效的一侧文字处于激活高亮状态(源码中的labelLeftKls/labelRightKls通过ns.is('active', ...)控制)。
inline-prompt:文字内嵌进滑块
当希望文字直接显示在滑块内部时,使用inline-prompt属性。此时需要注意官方文档与源码的明确限制:
仅渲染文字的第一个字符。
从 switch.vue 的模板可以看到,inline-prompt模式下内容走的是.el-switch__inner结构,且官方示例(如active-text="完整展示多个内容")实际展示出的效果是首字 + 省略。因此若状态文字超过 1 个字符(如中文的"是/否"、英文的"Y/N"),可以放心使用;若必须展示完整文案,请使用非inline-prompt的轨道两侧模式。
自定义图标(Custom icons)
active-icon与inactive-icon允许为开关两个状态指定图标,两者都会覆盖对应的文字属性(源码注释明确 "overridesactive-text" / "overridesinactive-text")。参见 custom-icons.vue 示例:
<script setup lang="ts"> import { ref } from 'vue' import { Check, Close } from '@element-plus/icons-vue' const value1 = ref(true) const value2 = ref(true) </script> <template> <el-switch v-model="value1" :active-icon="Check" :inactive-icon="Close" /> <el-switch v-model="value2" class="mt-2" style="margin-left: 24px" inline-prompt :active-icon="Check" :inactive-icon="Close" /> </template>图标属性的取值类型为string | Component(即IconPropType,见 switch.ts),你可以:
- 直接传入一个 SVG Vue 组件(如上例的
Check/Close); - 传入一个已全局注册的组件名字符串。
Element Plus 内置了丰富的图标库,可前往 图标文档 挑选。模板中图标通过<el-icon>包装<component :is="...">渲染,因此在inline-prompt模式下图标也能完整显示在滑块内部,不受"仅渲染首字符"限制的影响。
扩展的值类型(Extended value types)
默认情况下开关绑定的是布尔值,但通过active-value与inactive-value,你可以让开关承载Boolean、String或Number任意一种类型的值。参见 extended-value-types.vue 示例:
<el-tooltip :content="'Switch value: ' + value" placement="top"> <el-switch v-model="value" style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949" active-value="100" inactive-value="0" /> </el-tooltip> <script lang="ts" setup> import { ref } from 'vue' const value = ref('100') </script>此时v-model绑定的就不再是布尔值,而是开启时的"100"与关闭时的"0"。这与checked使用严格相等(===)比较的实现是配套的——类型必须完全一致才能正确匹配。在 switch.ts 中,activeValue默认true、inactiveValue默认false,类型均允许[Boolean, String, Number]。
这一能力在表单提交场景非常实用,例如直接把开关值对应到接口约定的枚举字符串或数字,无需在提交前再做一次布尔到具体值的转换。
禁用状态(Disabled)
添加disabled属性即可禁用开关,见 disabled.vue 示例:
<el-switch v-model="value1" disabled /> <el-switch v-model="value2" class="ml-2" />禁用后的开关透明度变为0.6(见 switch.scss)。值得注意的细节是,禁用状态并不仅仅是 UI 层面的:在 switch.vue 中,switchDisabled由useFormDisabled计算得出,它会同时考虑:
- 组件自身的
disabled属性; - 所在
el-form/el-form-item的禁用状态; loading状态也会强制禁用开关。
同时模板中的原生<input type="checkbox">也会同步设置:disabled="switchDisabled",保证键盘与鼠标都无法操作。
加载状态(Loading)
设置loading属性为true表示开关处于加载状态,见 loading.vue 示例:
<el-switch v-model="value1" loading /> <el-switch v-model="value2" loading class="ml-2" />加载状态下,滑块(.el-switch__action)内部会渲染一个Loading旋转图标(<el-icon v-if="loading"><loading /></el-icon>),并且如上一节所述,开关会同时进入禁用态,避免用户在异步请求期间反复切换。loading通常与before-change配合使用——先置loading为true表示请求进行中,请求结束后再复位。
阻止切换(Prevent switching)
before-change属性允许在状态真正改变之前插入一道"审批关卡"。官方定义:返回false或返回一个被 reject 的Promise,都会阻止切换。参见 prevent-switching.vue 示例:
<script setup lang="ts"> import { ref } from 'vue' import { ElMessage } from 'element-plus' const value1 = ref(false) const value2 = ref(false) const loading1 = ref(false) const loading2 = ref(false) // 返回 true -> 允许切换 const beforeChange1 = (): Promise<boolean> => { loading1.value = true return new Promise((resolve) => { setTimeout(() => { loading1.value = false ElMessage.success('Switch success') return resolve(true) }, 1000) }) } // reject -> 阻止切换 const beforeChange2 = (): Promise<boolean> => { loading2.value = true return new Promise((_, reject) => { setTimeout(() => { loading2.value = false ElMessage.error('Switch failed') return reject(new Error('Error')) }, 1000) }) } </script> <template> <el-switch v-model="value1" :loading="loading1" :before-change="beforeChange1" /> <el-switch v-model="value2" class="ml-2" :loading="loading2" :before-change="beforeChange2" /> </template>源码级的执行逻辑
before-change的判断逻辑在 switch.vue 的switchValue函数中:
const switchValue = () => { if (switchDisabled.value) return const { beforeChange } = props if (!beforeChange) { handleChange() return } const shouldChange = beforeChange() const isPromiseOrBool = [ isPromise(shouldChange), isBoolean(shouldChange), ].includes(true) if (!isPromiseOrBool) { throwError( COMPONENT_NAME, 'beforeChange must return type `Promise<boolean>` or `boolean`' ) } // ... }关键点:
- 返回类型强校验:
beforeChange必须返回boolean或Promise<boolean>,否则组件会直接throwError,属于运行时硬约束; - Promise 语义:
resolve(true)才执行切换,reject会被捕获并debugWarn(不会导致未处理的 Promise 异常),而resolve(false)同样不切换; - 同步 false:直接返回
false也能立即阻止切换; handleChange()内部会依次发出update:model-value、change、input三个事件。
这种"先校验、后变更"的模式非常适合需要权限校验、余额确认、二次弹窗确认等业务场景。
自定义滑块图标(Custom action icon)^(2.3.9)
active-action-icon与inactive-action-icon用于自定义滑块(action)内部显示的图标,与作用于轨道的active-icon/inactive-icon在视觉位置上不同。参见 custom-action-icon.vue 示例:
<script setup lang="ts"> import { ref } from 'vue' import { Hide, View } from '@element-plus/icons-vue' const value1 = ref(true) </script> <template> <el-switch v-model="value1" :active-action-icon="View" :inactive-action-icon="Hide" /> </template>从 switch.vue 模板 可以看到.el-switch__action内的渲染优先级为:
loading(加载图标) > active-action / inactive-action 插槽 > action-icon 属性即加载状态下始终显示 Loading 图标;否则优先使用插槽,其次才回退到active-action-icon/inactive-action-icon属性。
自定义滑块插槽(Custom action slot)^(2.4.4)
如果你需要的不是图标而是任意内容(如文字、徽标、自定义图形),可以使用active-action与inactive-action插槽。参见 custom-action-slot.vue 示例:
<el-switch v-model="value1"> <template #active-action> <span class="custom-active-action">T</span> </template> <template #inactive-action> <span class="custom-inactive-action">F</span> </template> </el-switch>插槽内容将直接渲染在滑块内部,其尺寸受.el-switch__action样式(宽高均为滑块按钮尺寸)约束,因此自定义内容需要注意缩放与溢出控制。
完整 API 参考
以下 API 以 switch.md 官方文档为准,并结合 switch.ts 源码中的默认值与校验逻辑补充说明。
Attributes
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| model-value / v-model | 绑定值,必须与active-value或inactive-value之一相等,默认布尔类型 | boolean / string / number | false |
| disabled | 是否禁用 | boolean | false |
| loading | 是否处于加载状态(加载时同时禁用) | boolean | false |
| size | 开关尺寸 | '' / 'large' / 'default' / 'small' | ''(跟随表单上下文) |
| width | 开关宽度 | number / string | '' |
| inline-prompt | 图标或文字是否显示在滑块内部(文字仅渲染第一个字符) | boolean | false |
| active-icon | 开启状态图标,覆盖active-text | string / Component | — |
| inactive-icon | 关闭状态图标,覆盖inactive-text | string / Component | — |
| active-action-icon ^(2.3.9) | 开启状态滑块内图标 | string / Component | — |
| inactive-action-icon ^(2.3.9) | 关闭状态滑块内图标 | string / Component | — |
| active-text | 开启状态文字 | string | '' |
| inactive-text | 关闭状态文字 | string | '' |
| active-value | 开启状态对应的值 | boolean / string / number | true |
| inactive-value | 关闭状态对应的值 | boolean / string / number | false |
| name | 原生 input 的 name 属性 | string | '' |
| validate-event | 是否触发表单校验 | boolean | true |
| before-change | 状态改变前的钩子;返回false或返回被 reject 的Promise时阻止切换 | () => Promise<boolean> / boolean | — |
| id | 原生 input 的 id | string | — |
| tabindex | 原生 input 的 tabindex | string / number | — |
| aria-label ^(a11y) ^(2.7.2) | 同原生 input 的aria-label | string | — |
| active-color ^(deprecated) | 开启状态背景色(已废弃,改用 CSS 变量--el-switch-on-color) | string | '' |
| inactive-color ^(deprecated) | 关闭状态背景色(已废弃,改用 CSS 变量--el-switch-off-color) | string | '' |
| border-color ^(deprecated) | 开关边框颜色(已废弃,改用 CSS 变量--el-switch-border-color) | string | '' |
| label ^(a11y) ^(deprecated) | 同原生 input 的aria-label(已废弃,使用aria-label) | string | — |
说明:
width属性在源码中通过addUnit(props.width)注入.el-switch__core的width内联样式(见 switch.vue),因此传60与'60px'均合法;传入数值时宽度会随尺寸变大,文字区域会自动以省略号截断(示例中width="60"配合长文本即为该效果)。
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| change | 值发生变化时触发 | (val: boolean / string / number) => void |
从 switch.ts 可以看到,组件内部实际上声明了三个事件:update:model-value、change、input,三者都校验参数类型必须为boolean / string / number之一。其中change事件在handleChange中与update:model-value、input一并触发,回调参数为切换后的新值(即activeValue或inactiveValue)。
Slots
| 插槽名 | 说明 | 可用版本 |
|---|---|---|
| active-action | 自定义开启状态下的滑块内容 | ^(2.4.4) |
| inactive-action | 自定义关闭状态下的滑块内容 | ^(2.4.4) |
| active | 自定义开启状态下的内容(轨道内/外由inline-prompt决定) | ^(2.13.0) |
| inactive | 自定义关闭状态下的内容 | ^(2.13.0) |
active/inactive插槽在模板中有两处消费:非inline-prompt时渲染在轨道两侧的标签区,inline-prompt时渲染在滑块旁的内部区域(见 switch.vue),因此可以通过这两个插槽在inline-prompt模式下绕过"仅首字符"限制。
Exposes
| 方法名 | 说明 | 类型 |
|---|---|---|
| focus | 手动聚焦到开关组件 | () => void |
focus()在源码中通过input.value?.focus?.()实现(见 switch.vue),配合模板中:focus-visible样式(见 switch.scss),键盘聚焦时滑块外层会出现--el-switch-on-color颜色的描边,保证可访问性。此外defineExpose还暴露了只读的checked计算属性,可直接通过 ref 判断开关当前是否处于开启状态。
无障碍与表单集成
从模板结构看(switch.vue),Switch 底层是一个不可见的原生<input type="checkbox">,并声明了role="switch"、aria-checked、aria-disabled、aria-label等 ARIA 属性,屏幕阅读器可以正确识别其开关语义。键盘上支持 Tab 聚焦与 Enter 键触发切换(@keydown.enter="switchValue")。
在表单场景中,Switch 通过useFormItem与useFormItemInputId与el-form-item深度集成:id 自动关联、禁用/尺寸自动继承、validate-event(默认true)在每次checked变化时触发formItem.validate('change'),因此不需要额外代码即可参与表单校验与 label 关联。
小结
Element Plus 的 Switch 组件在设计上兼顾了简单与灵活:
- 开箱即用:默认布尔绑定即可满足绝大多数场景;
- 值类型扩展:
active-value/inactive-value让开关可直接对接业务枚举值; - 状态控制:
disabled、loading、before-change三层机制保障了异步与受限场景的正确性; - 视觉自定义:图标、文字、滑块内容、轨道颜色(CSS 变量)均可按需定制;
- 可访问性:底层原生 checkbox + 完整 ARIA 语义,天然支持键盘操作与屏幕阅读器。
结合 switch.ts 与 switch.vue 的源码阅读,你不仅能准确使用每一个 API,还能理解"严格相等判状态、before-change 先行校验、三个事件同时触发"等底层约定,从而在复杂业务中做出正确的设计决策。
【免费下载链接】element-plus🎉 A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plus
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考