乐趣区

关于前端:小试牛刀的-Vue3-多选框

源码:地址

因为本人最近始终在应用 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>

实现成果

总结

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

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

退出移动版