源码:地址
因为本人最近始终在应用 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>
实现成果
总结
还是要本人入手封装,组件内的一些细节和思路对于业务逻辑还是有肯定的启发作用。
欢送关注【前端学啥】一起交换