复选框组件在日常的开发过程中, 也是一个很常见和常用的组件
这里我会使用两种方法来实现复选框组件的封装:
1. v-model
2. @vueuse/core
首先完成复选框组件的基础布局, 和点击切换状态的功能
思路分析:
1. 基础布局是由一个父盒子, 其中包含两个字体图标和一个默认插槽
2. 其中暴露一个点击事件 changeChecked , 用来修改字体图片的状态
3. 定义一个变量, 用户点击时; 对这个变量进行取反即可
<template>
<div class="xtx-checkbox" @click="changeChecked">
<i v-if="checked" class="iconfont icon-checked"></i>
<i v-else class="iconfont icon-unchecked"></i>
<span v-if="$slots.default"><slot /></span>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'XtxCheckbox',
setup () {
const checked = ref(false)
const changeChecked = () => {
checked.value = !checked.value
}
return { checked, changeChecked }
}
}
</script>
<style scoped lang="less">
.xtx-checkbox {
display: inline-block;
margin-right: 2px;
.icon-checked {
color: @xtxColor;
~ span {
color: @xtxColor;
}
}
i {
position: relative;
top: 1px;
}
span {
margin-left: 2px;
}
}
</style>
现在我们就来使用第一种方法实现复选框组件的封装(v-model)
首先我们得知道, 在 Vue3 中 v-model 其实是 :modelValue(默认的) + :update:modelValue的组合
思路分析:
1. 父组件会定义一个响应式数据, 通过 v-model 的方式传给子组件
2. 那么, 子组件内部就需要进行接收(默认是 modelValue)
3. 因为, 父组件传入的是一个布尔值; 所以子组件不能直接进行修改(违背单项数据流)
4. 复选框组件内部, 重新定义一个变量(checked)
5. 当 changeChecked 函数被调用的时候, 将 checked 的值 emit 出去
6. 可能在父组件中, v-model 依赖的值就发生了变化; 所以我们需要使用 watch 对父组件传入进来的值进行监听(且是立即执行)
7. 一旦传入的数据发生变化, 立即修改 checked 变量的值
比如说:
页面中有一个复选框, 前端向后端发送请求获取数据; 后端返回的数据是需要将复选框选中的
所以, 前端需要修改父组件中 v-model 所依赖的值
依赖值一发生变化, 复选框组件内部记录的值难道不要跟着变吗?
<template>
<div class="xtx-checkbox" @click="changeChecked">
<i v-if="checked" class="iconfont icon-checked"></i>
<i v-else class="iconfont icon-unchecked"></i>
<span v-if="$slots.default"><slot /></span>
</div>
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'XtxCheckbox',
props: {
modelValue: {
type: Boolean,
default: false
}
},
setup (props, { emit }) {
const checked = ref(false)
const changeChecked = () => {
checked.value = !checked.value
emit('update:modelValue', checked.value)
}
watch(() => props.modelValue, (newVal) => {
checked.value = newVal
}, { immediate: true })
return { checked, changeChecked }
}
}
</script>
<XtxCheckbox v-model="flag"></XtxCheckbox>
<script>
export default {
name: 'SubFilter',
setup () {
const flag = ref(false)
return { filterData, filterLoading, flag }
}
}
</script>
再来使用第二种方法实现复选框组件的功能(@vueuse/core)
<template>
<div class="xtx-checkbox" @click="changeChecked">
<i v-if="checked" class="iconfont icon-checked"></i>
<i v-else class="iconfont icon-unchecked"></i>
<span v-if="$slots.default"><slot /></span>
</div>
</template>
<script>
import { useVModel } from '@vueuse/core'
export default {
name: 'XtxCheckbox',
props: {
modelValue: {
type: Boolean,
default: false
}
},
setup (props, { emit }) {
// 得到 useVModel 包装之后的数据
const checked = useVModel(props, 'modelValue', emit)
const changeChecked = () => {
// checked.value 相当于在做在使用父组件的数据
// checked.value = newVal 相当于在做 emit 操作
const newVal = !checked.value
checked.value = newVal
// 让复选框组件兼容 change 事件
emit('change', newVal)
}
return { checked, changeChecked }
}
}
</script>