源码:地址

因为本人最近始终在应用 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 的解决和以前稍稍有点不同。

由文档所述,其中的 propvalue 变成了 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>

实现成果

总结

还是要本人入手封装,组件内的一些细节和思路对于业务逻辑还是有肯定的启发作用。

欢送关注【前端学啥】一起交换