文章目录
- 基础用法 modelValue
- 尺寸 size
- 文字描述与自定义图标
- 扩展的 value 类型
- isControlled 是否受控
- actualValue 实际值
- checked 开关状态
- 禁用状态、加载状态
- 阻止切换 beforeChange
- 切换 handleChange
看源码时候做的笔记。
Switch 开关 | Element Plus (element-plus.org)
基础用法 modelValue
在vue3中,
v-model
和modelValue
绑定的是同一个值。
v-model
是一个语法糖,等同于同时使用:modelValue
和@update:modalValue
,即向子组件传递一个名为modelValue
的prop,同时监听一个名为update:modelValue
的事件。
基础用法:绑定 v-model
到一个 Boolean 类型的变量。 会传到prop的modelValue中。
modelValue: {type: [Boolean, String, Number],default: false,},
尺寸 size
会传到prop的size中,是枚举值。
文字描述与自定义图标
- active-text
- inactive-text
- inline-prompt
分别表示开关文案、控制文本是否显示在点内。源码中是驼峰式的,主要通过条件渲染实现。
- inactive-icon
- active-icon
开关icon。条件渲染实现。
扩展的 value 类型
- activeValue
- inactiveValue
绑定的开/关的对应值,类型可以为:Boolean, String, Number。
/*** @description switch value when in `on` state*/activeValue: {type: [Boolean, String, Number],default: true,},/*** @description switch value when in `off` state*/inactiveValue: {type: [Boolean, String, Number],default: false,},
switch开关底层是一个vue的input checkbox
控件:
<input:id="inputId"ref="input":class="ns.e('input')"type="checkbox"role="switch":aria-checked="checked":aria-disabled="switchDisabled":aria-label="label || ariaLabel":name="name":true-value="activeValue":false-value="inactiveValue":disabled="switchDisabled":tabindex="tabindex"@change="handleChange"@keydown.enter="switchValue"/>
aria表示无障碍。
用true-value
的activeValue
和 false-value
的inactiveValue
替换 选中的true 和 未选中的false。
:true-value="activeValue"
:false-value="inactiveValue"
表单输入绑定 | Vue.js (vuejs.org)
因此传入的activeValue
和inactiveValue
属性就为switch开/关的值。
isControlled 是否受控
代码中定义了一个变量isControlled
表示是否受控。
const isControlled = ref(props.modelValue !== false)
定义类型时定义的modelValue
默认为false,若此时modelValue
不为false,说明switch组件是受控的,isControlled
为true,switch组件显示的值为传入值。
监听绑定的modelValue
属性,由于监听的是属性,因此要传入一个getter
。若modelValue发生变化,也说明此switch组件是受控的。
watch(() => props.modelValue,() => {isControlled.value = true}
)
actualValue 实际值
代码定义了一个变量actualValue
表示实际值。
若switch是受控组件,则它的实际值为绑定的modelValue
,否则为false。
若实际值actualValue
与绑定为开的值activeValue
相同,说明switch状态为开(checked=true)
const actualValue = computed(() => {return isControlled.value ? props.modelValue : false
})const checked = computed(() => actualValue.value === props.activeValue)
到这里,actualValue
的值可能是modelValue
或false,可能出现不是activeValue / inactiveValue
的情况(若不受控,则actualValue为false)。下面代码的目的是确保actualValue的值为activeValue / inactiveValue 。向父组件触发事件,入参为inactiveValue
if (![props.activeValue, props.inactiveValue].includes(actualValue.value)) {emit(UPDATE_MODEL_EVENT, props.inactiveValue)emit(CHANGE_EVENT, props.inactiveValue)emit(INPUT_EVENT, props.inactiveValue)
}
这三个事件分别对应:
export const UPDATE_MODEL_EVENT = 'update:modelValue'
export const CHANGE_EVENT = 'change'
export const INPUT_EVENT = 'input'
checked 开关状态
代码定义了一个变量checked
表示开关状态。
const checked = computed(() => actualValue.value === props.activeValue)
参与控制前文所说的文字描述的显示与否。监听checked的变化,同步到input控件。
validateEvent
即文档中的 validate-event
属性,表示是否触发表单验证。如果需要触发的话,就触发。
watch(checked, (val) => {input.value!.checked = valif (props.validateEvent) {formItem?.validate?.('change').catch((err) => debugWarn(err))}
})
禁用状态、加载状态
定义了表单的禁用状态。若switchDisabled.value
为true,则切换状态的方法switchValue
会直接return。
const switchDisabled = useFormDisabled(computed(() => props.loading))
加载状态直接用条件渲染,不赘述。
阻止切换 beforeChange
设置beforeChange属性,若返回 false 或者返回 Promise 且被 reject,则停止切换。
若设置了beforeChange,则在触发切换switch前要先执行beforeChange。
在props中解构出beforeChange
。若没有设置beforeChange,则直接切换。
beforeChange是一个返回Promise<boolean> | boolean>
的方法。若它通过类型校验isPromiseOrBool
,则执行此方法。若promise.resolve的值能通过if判断,则执行切换:handleChange
。
const switchValue = () => {if (switchDisabled.value) returnconst { beforeChange } = propsif (!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`')}if (isPromise(shouldChange)) {shouldChange.then((result) => {if (result) {handleChange()}}).catch((e) => {debugWarn(COMPONENT_NAME, `some error occurred: ${e}`)})} else if (shouldChange) {handleChange()}
}
beforeChange: {type: definePropType<() => Promise<boolean> | boolean>(Function),},
文档的例子非常清晰:
<template><el-switchv-model="value1":loading="loading1":before-change="beforeChange1"/><el-switchv-model="value2"class="ml-2":loading="loading2":before-change="beforeChange2"/>
</template>
<script lang="ts" setup>
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)const beforeChange1 = () => {loading1.value = truereturn new Promise((resolve) => {setTimeout(() => {loading1.value = falseElMessage.success('Switch success')return resolve(true)}, 1000)})
}const beforeChange2 = () => {loading2.value = truereturn new Promise((_, reject) => {setTimeout(() => {loading2.value = falseElMessage.error('Switch failed')return reject(new Error('Error'))}, 1000)})
}
</script>
切换 handleChange
val 是inactiveValue / activeValue 。在下一个 DOM 更新周期更新 input.value.checked 的值
const handleChange = () => {const val = checked.value ? props.inactiveValue : props.activeValue// 向外暴露一个input事件,参数为valemit(UPDATE_MODEL_EVENT, val)emit(CHANGE_EVENT, val)emit(INPUT_EVENT, val)// 在下一个 DOM 更新周期更新 input.value.checked 的值nextTick(() => {input.value!.checked = checked.value})
}
emit(CHANGE_EVENT, val)
即文档中对外暴露的change事件。
其他内容,如switchKls
这种使用ns
生成动态的类名等,在之前的系列里提到过,不再赘述。