vue 使用hook 对 chekbox 做简单的逻辑抽离,一个核心多套模板

news2025/7/11 3:31:06

现在的组件库都会包含些相同的基础组件,功能大差不差,只是不同UI规范下的具体实现。这些基础组件基本能满足大部分的开发需求。 但世上无银弹,有时我们需要对组件做细微的调整可能是功能上的,可能是UI上的,例如 tab切换,需要自定义标签头,修改标签布局,显示或隐藏等,这些功能组件都是在大致的功能基础上做的定向扩展或修改。如果每次都重新编写一套逻辑就显得累赘,繁琐。

在vue3提供hook的逻辑复用方式后,我们能通过hook将公共逻辑抽离出来提高复用, 小的功能业务逻辑可以,基础组件交互逻辑当然也可以。 这里就遇到的一个菜单的单选,复选需求, 为了能以后快速开发类似的组件功能。尝试将逻辑封装成hook。 以hook提供的API 在实现不同的组件。

需求

开始需要明确我们需要的基础功能点,功能尽量基础才能保证通用性,后续的功能也能在基础功能上做扩展.

  • 单选
  • 多选
  • 跨组件通信

单选,多选是基础能力, 跨组件是为了将实际的值控制与独立的 checkItem 分离,这样checkItem可以不受 html 层级限制

可以多种不同的checkItem切换或嵌套使用

checkContainer -> [ checkItemType1, checkItemType2 ] 

使用例子

实现多选组件

容器组件 chekcboxContainer

<script setup>
import { useCheckboxContainer } from '@/hooks/useCheckbox'
import { toRefs } from 'vue'
import { PROVIDE_KEY } from './common'


const props = defineProps({value: {type: Array,default: () => ([])}
})


const emit = defineEmits(['input', 'change'])
const { value } = toRefs(props)
// 绑定 v-model 等事件
function selectHandler (selected) {emit('input', selected)emit('change', selected)
}
// 调用钩子逻辑
useCheckboxContainer({setCb: selectHandler,provideKey: PROVIDE_KEY,valueRef: value
})


</script>
<template><div class="container"><slot></slot></div>
</template>
<style lang="scss" scoped>
.container{overflow: hidden;border-radius: 4px;border: 1px solid #00385a;
}
</style> 

子项 chekcboxItem

<script setup>
import { ref } from 'vue'
import { useCheckboxItem } from '@/hooks/useCheckbox'
import { PROVIDE_KEY } from './common'


const emit = defineEmits(['change'])


const props = defineProps({label: {type: String,default: ''},value: {type: [Number, String],default: ''}
})


const { value, label } = toRefs(props)


const {active,pushItem,updateActive
} = useCheckboxItem({injectKey: PROVIDE_KEY,value: unref(value)
})


// 先容器注册子项
pushItem(unref(value), {value: unref(value),label: unref(label)
})


// 绑定激活样式类
const cls = computed(() => {return {active: unref(active)}
})
// 触发选择事件
function clickHandler(){updateActive(unref(value))emit('change', value)
}


</script>
<template><div class='checkbox-item' :class="cls" @click="clickHandler"><slot>{{ label }}</slot></div>
</template>
<style lang='scss' scoped>
.checkbox-item{padding: 8px 16px;border: 1px solid #eee;border-radius: 4px;color: #333;
}
.active{background: orange;color: #fff;
}


</style> 

组件使用

const selected = ref([])


<ChekcboxContainer v-model='selected'><ChekcboxItem value='red'> red </ChekcboxItem><ChekcboxItem value='orange'> orange </ChekcboxItem><ChekcboxItem value='blue'> blue </ChekcboxItem>
</ChekcboxContainer> 

useCheckbox 实现

这里将功能才分到 useCheckboxContainer useCheckboxItem 两个hook中, useCheckboxContainer 包含主要逻辑 useCheckboxItem 用于触发选择,显示激活状态

import useBool from '../useBool'
import { shallowRef, inject, provide, computed, unref, watch } from 'vue'
import {unique,merge
} from './utils'

/**
 * 新旧响应值标识符,防止无限循环更新组件内外值
 */
export const OLD_ACTIVE_VALUE_MAKR = Symbol('OLD_ACTIVE_VALUE_MAKR')
export const DEFAULT_SIGN = Symbol('DEFAULT_SIGN')
export const DEFAULT_CONTAINER_OPTIONS = {provideKey: DEFAULT_SIGN
}

/**
 * 多选容器钩子
 * @summary
 * 提供 1.多选 2.全选
 * 通过依赖注入,保证子项与容器的通信。所以需要提供依赖注入标识
 * @param options
 * @param options.provideKey 依赖注入标识
 * @param options.valueRef props 接收的已选列表
 * @param options.setCb 更新已选值回调
 * @returns [ state, tools ]返回内部状态,工具函数
 * state:
 *- allCheck 全选状态
 *- activeList 已选列表
 * tools:
 *- closeValueWatch 清除 valueRef 监听
 *- updateActive 更新当前已选项, 1.已选列表无已选时,添加。 2.已选列表存在时,移除
 *- pushItem 向容器中,注册子项信息
 *- checkAll 全选
 *- clearAll 全清空
 *- setAllCheck allCheck设为true
 *- removeAllCheck allCheck设为false
 *- updateAllCheck 通过已选项更新 allCheck 状态
 *- closeAllCheckStatusWatch 清除 allCheck 监听
 *
 */
export function useCheckboxContainer (options = {}) {const { provideKey, valueRef, setCb } = merge(DEFAULT_CONTAINER_OPTIONS, options)const activeList = shallowRef([])const [allCheck, { setTrue: setAllCheck, setFalse: removeAllCheck }] = useBool()const allItem = new Map([])function pushItem (key, item) {allItem.set(key, item)}function checkAll () {activeList.value = [...allItem.keys()]setAllCheck()}function clearAll () {activeList.value = []removeActive()}function addSign (list) {list[OLD_ACTIVE_VALUE_MAKR] = truereturn list}function hasItem (item) {return unref(activeList).includes(item)}function addActive (value) {activeList.value = unique([...unref(activeList), value])}function removeActive (value) {activeList.value = unref(activeList).filter(i => i !== value)}function updateActive (value) {hasItem(value) ? removeActive(value) : addActive(value)updateAllCheck()setCb && setCb(addSign([...unref(activeList)]))}function updateAllCheck () {allCheck.value = unref(activeList).length === [...allItem.keys()].length}const closeAllCheckStatusWatch = watch(allCheck, (newVal, oldVal) => {if (newVal === oldVal) {return}newVal ? checkAll() : clearAll()})// 同步预设值 or 外部动态修改const closeValueWatch = watch(valueRef, (newVal) => {if (newVal[OLD_ACTIVE_VALUE_MAKR] && newVal.length === unref(activeList).length) {return}activeList.value = [...unref(valueRef)]}, { immediate: true })provide(provideKey, {hasItem,activeList,updateActive,pushItem})return [{allCheck,activeList},{closeValueWatch,updateActive,pushItem,checkAll,clearAll,setAllCheck,removeAllCheck,updateAllCheck,closeAllCheckStatusWatch}]
}


export const DEFAULT_ITEM_OPTIONS = {injectKey: DEFAULT_SIGN
}

/**
 * 多选子项钩子
 * @param options
 * @param options.injectKey 依赖注入标识
 * @param options.value 当前项值or标识
 * @returns
 * - active 当前项是否已选
 * - hasItem 已选判断函数
 * - activeList 已选列表
 * - updateActive 更新已选项
 * - pushItem 向容器注册子项
 */
export function useCheckboxItem (options = {}) {const _options = merge(DEFAULT_ITEM_OPTIONS, options)const {injectKey,value} = _optionsconst injecteStore = inject(injectKey)const active = computed(() => {return injecteStore.hasItem(value)})return {active,...injecteStore}
} 

utils

export function unique (list) {return Array.from(new Set(list))
}


export function isUndefined (v) {return v === undefined
}


// 混合非 undefined 属性
export function merge (source = {}, target = {}) {const keys = new Set([...Object.keys(source), ...Object.keys(target)])return [...keys].reduce((acc, prop) => {return {...acc,[prop]: isUndefined(source[prop]) ? target[prop] : source[prop]}}, {})
} 

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/168027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaWeb基础(三) Request和Response详解

JavaWeb基础(三) Request和Response详解 1&#xff0c;Request和Response的概述 Request是请求对象&#xff0c;Response是响应对象。 此时&#xff0c;我们就需要思考一个问题request和response这两个参数的作用是什么? request: 获取请求数据 浏览器会发送HTTP请求到后台…

跨站脚本攻击漏洞(XSS)-基础篇

数据来源 跨站脚本攻击 1、什么是跨站脚本攻击? 跨站脚本( Cross-site Scripting)攻击&#xff0c;攻击者通过网站注入点注入客户端可执行解析的 payload&#xff08;脚本代码&#xff09;&#xff0c;当用户访问网页时&#xff0c;恶意 payload自动加载并执行&#xff0c;…

索引(index)

索引&#xff08;index&#xff09; 1、什么是索引&#xff1a; 索引是在数据库表的字段上添加的&#xff0c;是为了提高查询效率存在的一种机制。一张表的一个字段可以添加一个索引&#xff0c;当然多个字段联合起来也可以添加索引&#xff0c;索引相当于一本书的目录&#xf…

Spring Boot 3 步完成日志脱敏,简单实用!

在我们写代码的时候&#xff0c;会书写许多日志代码&#xff0c;但是有些敏感数据是需要进行安全脱敏处理的。 对于日志脱敏的方式有很多&#xff0c;常见的有&#xff1a; 使用conversionRule标签&#xff0c;继承MessageConverter 书写一个脱敏工具类&#xff0c;在打印日志…

springboot+mybatisplus实现分页

在日常开发中&#xff0c;多记录的列表查询可能会遇到分页处理的场景&#xff0c;在springboot项目中传统是引入mybatis组件进行持久化&#xff0c;然后通过pagehelper组件进行分页实现。下面体验一下在springboot项目中引入mybatisplus组件&#xff0c;通过其自带分页插件实现…

cpu简述--指令集架构

很多初级开发者其实都对cpu了解不多&#xff0c;个人兴趣原因想要了解一下cpu的相关知识&#xff0c;所以开几篇文章记录一下吧。 2002年8月10日,中国科学院计算技术研究所的青年科学家胡伟武带领研制组,研制出我国首枚拥有自主知识产权的通用高性能微处理芯片——“龙芯一…

MindOpt安装配置教程(Windows系统)

1 前言 官网有很多的说明文档、教程&#xff0c;但是可能有些地方&#xff08;这里仅仅补充安装配置部分&#xff0c;其他操作建议自行去官网进行探索&#xff09;不是很详细&#xff0c;踩了一些坑&#xff0c;所以进行了一些总结。 2 下载安装 url&#xff1a;求解器SDK下载…

Nginx基础02:配置文件nginx.conf(Part1)

我们使用Nginx主要是通过其配置文件nginx.conf来实现的。按照一定的规则&#xff0c;编写特定的指令&#xff0c;可以帮助我们实现对Web服务的控制&#xff01;所以&#xff0c;学习Nginx的用法&#xff0c;几乎就是学习nginx.conf&#xff01;如何使用本篇文章本文作为一篇高度…

【博客590】iptables raw表的特殊作用

iptables raw表的特殊作用 1、iptables四表五链&#xff1a; 2、raw表的优先级 从上图中可以看到raw表作用于prerouting和output链&#xff0c;且在这两个链中的几个表中拥有最高优先级&#xff0c;并且是高于连接跟踪的&#xff0c;这个也是raw表用于优化性能的一个重要前提 …

Leetcode链表专题专练-万字总结

文章目录 系列&#xff1a;链表专练 语言&#xff1a;java & go 题目来源&#xff1a;Leetcode 常考点&#xff1a; 单链表 & 双链表 &双指针 思路和参考答案文章目录链表专题总结链表专练链表专题总结 链表是一种通过指针串联在一起的线性结构&#xff0c;每一个…

【计算机网络】传输层协议-------TCP详解

文章目录1. TCP 协议概述2. TCP原理2.1 保持可靠性的机制2.1.1 确认应答2.1.2 超时重传2.1.3 连接管理机制(安全机制)2.1.3.1 三次握手2.1.3.2 四次挥手2.1.4 滑动窗口2.1.5 流量控制2.1.6 拥塞控制2.1.7延时应答2.1.8 捎带应答2.1.9 粘包问题2.1.10 TCP异常2.1.11 TCP vs UDP1…

记录每日LeetCode 112.路径总和 Java实现

题目描述&#xff1a; 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶…

马蜂窝如何利用 APISIX 网关实现微服务架构升级

作者&#xff1a;董红帅&#xff0c;马蜂窝微服务体系建设以及基础服务能力建设专家。 马蜂窝作为旅行社交平台&#xff0c;是数据驱动的新型旅行电商。基于十余年的内容积累&#xff0c;马蜂窝通过 AI 技术与大数据算法&#xff0c;将个性化旅行信息与来自全球各地的旅游产品供…

Water Pamola通过恶意订单对电商发起攻击

自2019年以来&#xff0c;趋势科技的研究人员一直在追踪一个被称为“Water Pamola”的攻击活动。该活动最初通过带有恶意附件的垃圾邮件攻击了日本、澳大利亚和欧洲国家的电子商务在线商店。 但是&#xff0c;自2020年初以来&#xff0c;研究人员注意到Water Pamola的活动发生…

(二十三)Collecttion集合

目录 前言: ①Collecttion集合的体系结构 ②Collecttion集合的遍历方式 方式一:迭代器 方式二:foreach/增强for循环 方式三:Lambda表达式 ③常见数据结构 前言: Collection&#xff1a; 是所有集合的顶级接口,里面规定了集合操作元素的相关功能方法集合与数组一样,用于存储一组…

使用Sa-token实现单点登录

使用Sa-token实现单点登录单点登录需求为何选择Sa-Token简单使用sa-token接口如何保持登录态使用拦截器实现鉴权聊聊Sa-Token的理解聊聊遇到的一些问题单点登录需求 其实一直想写一个单点登录系统&#xff0c;现在的现状是公司内部有非常多项目的&#xff0c;然后每个项目一套登…

数据分析面试题--数理知识点1

目录标题1&#xff0c;python统计一段话每个单词出现的次数2&#xff0c;SQL中如何利用replace函数统计给定重复字段在字符串中的出现频率&#xff1f;3&#xff0c;常见的统计分析方法有哪些&#xff1f;拿到数据如何分析4&#xff0c;参数估计和假设检验的联系和区别5&#x…

网络实验之OSPF路由协议(一)

一、OSPF路由协议简介 开放式最短路径优先&#xff08;Open Shortest Path First&#xff0c;OSPF&#xff09;路由协议是用于网际协议&#xff08;IP&#xff09;网络的链路状态路由协议。该协议使用链路状态路由算法的内部网关协议&#xff08;IGP&#xff09;&#xff0c;在…

NIO笔记

一. NIO 基础 non-blocking io 非阻塞 IO 1. 三大组件 1.1 Channel & Buffer channel 有一点类似于 stream&#xff0c;它就是读写数据的双向通道&#xff0c;可以从 channel 将数据读入 buffer&#xff0c;也可以将 buffer 的数据写入 channel&#xff0c;而之前的 st…

SAP ABAP 代码修改自动比较对象版本一致

第一步&#xff0c;找到SE38/SE37代码修改的出口 SMOD中查找 第二步&#xff0c;实施增强 CMOD中添加增强并激活&#xff0c;如下图 第三步&#xff0c;添加代码 如上图两个双击添加并修改代码 ZXSEUU08中与 ZXSEUU01代码一致&#xff0c;如下 *&----------------------…