源码:地址
因为本人最近始终在应用 Vue3
,本着学了不必就作废的准则就本人尝试着写了 UI 组件示例。而刚开始抉择的就是多选框这个组件。
多选框 Checkbox
组件是平时应用频率蛮高的一个组件,咱们当初一步步来欠缺本人的组件。
开始
先定义组件
<ani-checkbox> 选项 </ani-checkbox>
创立组件 Checkbox.vue
文件
<template>
<label
class="checkbox-wrap"
>
<input
type="checkbox"
v-model="model"
/>
<i class="check-icon">✓</i>
<slot></slot>
</label>
</template>
对 Checkbox
进行丑化,次要先暗藏 input[type=checkbox]
再对 input[type=checkbox]:checked
选择器进行解决
<style lang="less" scoped>
@color: #333;
@activeColor: #409eff;
.checkbox-wrap {
margin-right: 15px;
font-size: 14px;
user-select: none;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
.check-icon {
margin-right: 5px;
font-size: 14px;
font-style: normal;
display: inline-block;
width: 14px;
height: 14px;
text-align: center;
line-height: 14px;
color: #fff;
border: 1px solid @color;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
input[type='checkbox'] {display: none;}
input[type='checkbox']:checked + .check-icon {
background-color: @activeColor;
border-color: @activeColor;
}
&.is-checked-text {color: @activeColor;}
}
</style>
成果如下
逻辑解决
先定义 props
<ani-checkbox v-model="checked" @change="onChange"> 选项 </ani-checkbox>
咱们晓得 Vue3
对于 v-model
的解决和以前稍稍有点不同。
由文档所述,其中的 prop
的 value
变成了 modelValue
,所以咱们组件内定义的 props
须要扭转
props: {
modelValue: {type: [Boolean, Number, String],
default: () => undefined}
}
咱们利用 computed
实现双向绑定
setup(props, { emit}) {
const model = computed({get() {return props.modelValue;},
set(val) {emit("update:modelValue", val);
},
});
return {model}
}
再增加事件回调
<input type="checkbox" v-model="model" @change="onChange"/>
这里须要留神,emit
事件时须要在 emits
中注册
emits: ['change'],
setup(props, { emit}) {
...
const onChange = () => {emit('change', model.value);
}
return {onChange}
},
由此,一个简略的 checkbox
组件就实现了。不过咱们当初应用的 composition-api
模式,咱们能够封装一下整个逻辑 useCheckbox
函数
import {getCurrentInstance} from 'vue';
export function useCheckbox(props) {const { proxy} = getCurrentInstance()
const model = computed({get() {return props.modelValue;},
set(val) {proxy.emit("update:modelValue", val);
},
});
const onChange = () => {proxy.emit('change', model.value);
}
return {
model,
onChange
}
}
再在组件中引入
import {useCheckbox} from './useCheckbox';
setup(props) {return useCheckbox(props);
}
多选框组
咱们应用了很多 UI 库组件,checkbox
存在多选,大多数组件库也有这个多选组,咱们接下来封装一个多选框组。
新建一个 checkbox-group.vue
文件
<template>
<div class="ani-checkbox-group">
<slot></slot>
</div>
</template>
而理论应用时,咱们利用 checkbox-group
包裹 checkbox
组件
<ani-checkbox-group v-model="checkList">
<ani-checkbox> 选项一 </ani-checkbox>
<ani-checkbox> 选项二 </ani-checkbox>
<ani-checkbox> 选项三 </ani-checkbox>
</ani-checkbox-group>
模板文件咱们曾经写完了,接下来咱们来欠缺逻辑;这里利用 provide/inject
来传递父组件参数
export default {
name: 'AniCheckboxGroup',
props: {
modelValue: {type: [Array],
default: () => undefined}
},
emits: ['change'],
setup(props, ctx) {
// 定义事件
const changeEvent = value => {ctx.emit('update:modelValue', value);
nextTick(() => {ctx.emit('change, value);
});
};
const modelValue = computed({get() {return props.modelValue;},
set(val) {changeEvent(val);
}
});
// 向子组件传递
provide('CheckboxGroup', {
name: 'CheckboxGroup',
modelValue,
...toRefs(props),
changeEvent
});
}
}
子组件中 inject
接管
// 接管父组件音讯
export const useCheckboxGroup = () => {
// 这里的名称对应 provide 名称
const checkboxGroup = inject('CheckboxGroup', {});
// 判断是否为多选框组
const isGroup = computed(() => checkboxGroup && checkboxGroup.name === 'CheckboxGroup'
);
return {
isGroup,
checkboxGroup
};
};
useCheckbox
中须要判断是否为多选框组,具体思路看正文吧🤣,其实逻辑也简略~
export const useCheckbox = props => {const { emit} = getCurrentInstance();
const {isGroup, checkboxGroup} = useCheckboxGroup();
// 存在多选组,则应用多选组 modelValue
const store = computed(() =>
checkboxGroup ? checkboxGroup.modelValue.value : props.modelValue
);
// 还是判断多选组
const model = computed({get() {return isGroup.value ? store.value : props.modelValue;},
set(val) {if (isGroup.value && Array.isArray(val)) {checkboxGroup.changeEvent(val);
} else {emit('update:modelValue', val);
}
}
});
// 判断多选框是否选中
const isChecked = computed(() => {
const value = model.value;
if (isPropType(value, 'boolean')) {return value;} else if (isPropType(value, 'array')) {return value.includes(props.label);
}
return null;
});
const onChange = e => {
const target = e.target;
const value = target.checked ? true : false;
emit('change', value, e);
};
return {
model,
isChecked,
onChange
};
};
这里模板须要批改,咱们须要传入选中的选项字段 value
和多选框是否选中 checked
<label
class="checkbox-wrap"
>
<input
type="checkbox"
v-model="model"
:value="label"
:checked="isChecked"
@change="onChange"
/>
<i class="check-icon">✓</i>
<slot></slot>
</label>
实现成果
总结
还是要本人入手封装,组件内的一些细节和思路对于业务逻辑还是有肯定的启发作用。
欢送关注【前端学啥】一起交换