vue3+Ts+Hook的方式实现商城核心功能sku选择器

Hooks是React等函数式编程框架中非常受欢迎的工具,随着VUE3 Composition API 函数式编程风格的推出,现在也受到越来越多VUE3开发者的青睐,它让开发者的代码具有更高的复用度且更加清晰、易于维护。

本文将通过CRMEB商城商品详情sku选择功能了解Hooks的使用基础以及自定义HOOK开发相关的要点,快速入门。

a5943202401031729583572.jpg

Hook简介

1.什么是hook

Hooks并不是VUE特有的概念,实际上它原本被用于指代一些特定时间点会触发的勾子。而在React16之后,它被赋予了新的意义:

一系列以 use 作为开头的方法,它们提供了让你可以完全避开 class式写法,在函数式组件中完成生命周期、状态管理、逻辑复用等几乎全部组件开发工作的能力

在VUE3中,Hooks的概念结合了VUE的响应式系统,被称为组合函数。组合函数是VUE3组合式API中提供的新的逻辑复用的方案,是一类利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数,简单来说,它就是一个创建工具的工具.

2.Hooks与composition Api

Hooks是一种基于闭包的函数式编程思维产物,所以通常我们会在函数式风格的框架或组件中使用Hook,比如VUE的组合式API(Composition Api)。Hooks在VUE2所使用的选项式风格API中也不是不可以使用,毕竟Hook本质只是一个函数,只要hook内部所使用的api能够得到支持,我们可以在任何地方使用它们,只是可能需要额外的支持以及效果没有函数式组件中那么好,因为仍会被选项分割。

VUE3推出时为开发者带来了全新的Composition API即组合式API。它是一种通过函数来描述组件逻辑的开发模式。组合式API为开发者带来了更好的逻辑复用能力,通过组合函数来实现更加简洁高效的逻辑复用。

为什么要使用Hooks

在以往VUE2的选项式API中,主要通过Mixin或是Class继承来实现逻辑复用,但这种方式有三个明显的短板:

1.不清晰的数据来源:当使用了多个mixin/class时,哪个数据是哪个模块提供的将变得难以追寻,这将提高维护难度

2.命名空间冲突:来自多个class/mixin的开发者可能会注册同样的属性名,造成冲突

3.隐性的跨模块交流:不同的mixin/class之间可能存在某种相互作用,产生未知的后果

以上三种主要的缺点导致在大型项目的开发中,多mixin/class的组合将导致逻辑的混乱以及维护难度的提升,因而在VUE3的官方文档中不再继续推荐使用,保留mixin也只是为了迁移的需求或方便VUE2用户熟悉。

mixin的缺点其实就是Hooks的优点:

1.清晰一目了然的源头

2.没有命名冲突的问题

3.精简逻辑

怎么开始玩Hooks

Hooks的各类规范

1.通常来讲,一个Hook的命名需要以use开头,比如useTimeOut,这是约定俗成的,开发者看到useXXX即可明白这是一个Hook。Hook的名称需要清楚地表明其功能。

2.只在当前关注的最顶级作用域使用Hook,而不要在嵌套函数、循环中调用Hook

3.函数必须是纯函数,没有副作用

4.返回值是一个函数或数据,供外部使用

5.Hook内部可以使用其他的Hook,组合功能

6.数据必须依赖于输入,不依赖于外部状态,保持数据流的明确性

7.在Hook内部处理错误,不要把错误抛出到外部,否则会增加hook的使用成本

8.Hook是单一功能的,不要给一个Hook设计过多功能。单个Hook只负责做一件事,复杂的功能可以使用多个Hook互相组合实现,如果给单个Hook增加过多功能,又会陷入过于臃肿、使用成本高、难维护的问题中

下面通过一个简单的hooks感受一下它的魅力:

这是一个控制页面弹窗或者抽屉显示或隐藏的hook,在以往vue2中,我们实现这样一个功能,需要在data中定义一个变量,在methods中大概率会写两个方法分别控制弹窗的显示和隐藏,如果页面有多个这样的显隐组件,我们的代码简直是灾难,糟糕的事,我们的代码中这样的案例实在是太多了,有了hooks就完全不一样了.

这是一个useBoolean的hooks,可以看到它抛出了一个响应式的布尔值和四个方法.在使用的组件内就可以多次使用该方法,从而简化代码

import { ref } from 'vue';/*** boolean组合式函数* @param initValue 初始值*/
export default function useBoolean(initValue = false) {const bool = ref(initValue);function setBool(value: boolean) {bool.value = value;}function setTrue() {setBool(true);}function setFalse() {setBool(false);}function toggle() {setBool(!bool.value);}return {bool,setBool,setTrue,setFalse,toggle,};
}

Copy

3e1e0202401031733568131.png

通过这个例子发现,我们在vue2中大概率要写6个方法和定义三个变量的工作在vue3配合Hooks的情况下,三行代码就实现了.

下面进入我们本文的重点,通过hooks的方式实现sku选择器的功能.

a183f202401031730208119.png

在CRMEB各个项目中,加购功能并不是只有在商品详情页使用,还有很多页面也有使用,比如商品分类的几个模板,购物车页面,搭配购等,都会需要到打开sku选择商品规格的功能,改功能包含选择商品规格,价格,库存,规格图跟随切换实时变化,还有加购数量的操作,对库存为0的规格做不可操作的限制等等,所以这段代码在前端是非常臃肿庞大的一部分代码,牵扯的业务复杂,功能广泛,若是在需要的组件内每次复制粘贴,代码量就会非常庞大,所以若是可以将这部分功能单独抽离出来整理为一个可调用的方法就非常适合我们的使用场景.

先截图看看以前vue2的方式书写的该段代码.

bbf1a202401031712117992.png

f4c36202401031712371200.png

下面是我用vue3+ts+hooks的方式实现一下,代码如下:

import { ref, reactive, watch, unref } from 'vue';
import { cloneDeep } from 'lodash-es';
export default function useSkuSelect(productInfo: Product.Details) {watch(productInfo, () => {attr.productAttr = cloneDeep(productInfo.productAttr);DefaultSelect();});// 向sku选择器传递的数据const attr = reactive({productAttr: [],productSelect: createDefaultModel(),});const attrTxt = ref('请选择');const attrValue = ref('');attr.productAttr = productInfo.productAttr;function DefaultSelect() {let productAttr = attr.productAttr;let valueObj: Array = [];let value: Array = [];let productValue = productInfo.productValue;for (const key in productValue) {if (Object.prototype.hasOwnProperty.call(productValue, key)) {const element = productValue[key];if (element.stock > 0) {valueObj = attr.productAttr.length ? key.split(',') : [];break;}}}// 处理已售罄时默认选中第一个if (!valueObj.length && productAttr.length) {// value = Object.keys(productValue)[0].split(',');} else {value = valueObj;}for (let index = 0; index < productAttr.length; index++) {productAttr[index]!.index = value[index];}// 排序type selectPro = Pick;let productSelect: selectPro = productValue[value.join(',')];if (productSelect && productAttr.length) {attr.productSelect = createProductSelect(1, productSelect);attrValue.value = value.join(',');attrTxt.value = '已选择';} else if (!productSelect && productAttr.length) {attr.productSelect = createProductSelect(2, productSelect);attrValue.value = '';attrTxt.value = '请选择';} else if (!productSelect && !productAttr.length) {attr.productSelect = createProductSelect(3, productSelect);attrValue.value = '';attrTxt.value = '请选择';}}function attrVal(val: Product.AttrVal) {const { index, indexn } = val;const attrValue = attr.productAttr[index]!.attr_values[indexn];attr.productAttr[index]!.index = attrValue;}function ChangeAttr(res: any) {let productSelect = productInfo.productValue[res];if (productSelect && productSelect.stock >= 0) {attr.productSelect = createProductSelect(1, productSelect);attrValue.value = res;attrTxt.value = '已选择';} else {attr.productSelect = createProductSelect(2, productSelect);attrValue.value = '';attrTxt.value = '请选择';}}/**** @param type* true 加* false 减*/function changeCartNum(type: boolean) {// 获取当前变动属性let proSelect = productInfo.productValue[unref(attrValue)];//无属性值即库存为0;不存在加减;if (!proSelect) return;let stock = proSelect.stock || 0;if (attr.productSelect.cart_num) {if (type) {attr.productSelect.cart_num++;if (attr.productSelect.cart_num > stock) {attr.productSelect.cart_num = stock ? stock : 1;}} else {if (attr.productSelect.cart_num <= 1) {attr.productSelect.cart_num = 1;} else {attr.productSelect.cart_num--;}}}}function createProductSelect(type: number, productSelect: any): Product.selectPro {let proSelect: Product.selectPro = createDefaultModel();if (type === 1) {proSelect = {store_name: productInfo.storeInfo.store_name,image: productSelect.image,price: productSelect.price,stock: productSelect.stock,unique: productSelect.unique,cart_num: 1,vip_price: productSelect.vip_price,};} else if (type === 2) {proSelect = {store_name: productInfo.storeInfo.store_name,image: productInfo.storeInfo.image,price: productInfo.storeInfo.price,stock: 0,unique: '',cart_num: 0,vip_price: productInfo.storeInfo.vip_price,};} else if (type === 3) {proSelect = {store_name: productInfo.storeInfo.store_name,image: productInfo.storeInfo.image,price: productInfo.storeInfo.price,stock: productInfo.storeInfo.stock,unique: '',cart_num: 1,vip_price: productInfo.storeInfo.vip_price,};}return proSelect;}function createDefaultModel(): Product.selectPro {return {store_name: '',image: '',price: '',stock: 0,vip_price: '',unique: '',cart_num: 0,};}return {ChangeAttr,attrVal,changeCartNum,attrValue,attrTxt,attr,};
}

Copy

在使用sku选择器组件的页面上使用:

08ddd202401031735273907.png

1228d20240103173613725.png

这是一个管理sku选择器内商品规格选择的Hook,在使用时只需传入该商品的详情数据以及一些配置项即可快默认选中,节省了大量重复的控制代码,使用该Hook后只需调用useSkuSelect即可实现规格的切换,加购数量的控制等等,且继承原接口的类型.因为本人其实也是hooks小白,处于学习阶段,书写的该hook和ts代码有可能并不规范,欢迎读者交流指正.

总结

Hooks是VUE3中利用组合式API响应式的特性的,实现简单高效的逻辑复用、提高开发效率、提高VUE模块可维护性的工具。Hooks的组合可以让组件低代价、高效率地实现高复杂度业务,Hooks之间通常相互独立,没有过度耦合,降低后期陷入维护地狱的风险,而且可以使得功能模块更加易于测试.使用开源的Hook将为开发带来很多方便,而开发自定义Hook则需要花费一些时间,但在实现后,高度的定制化将为项目开发带来巨大的便利.Hooks的出现不意味着抛弃Class,Hooks也有自己的缺点比如内存泄漏和可能的性能问题。Class更加易于上手,在经验丰富、技术深厚的开发者手中也可以一定程度上避开Class的缺点

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【IPC通信--信号】

信号处理函数 • 信号发送函数 – kill(), sigqueue(), raise(), alarm(), setitimer(), pause() &#xff0c; abort() • 信号安装函数 – signal(), sigaction() • 信号集操作函数 – sigemptyset(), sigfillset(), sigaddset(), sigdelset(), sigismember() 信号发送函数—…

由浅入深理解C#中的事件

目录 本文较长&#xff0c;给大家提供了目录&#xff0c;可以直接看自己感兴趣的部分。 前言有关事件的概念示例​ 简单示例​ 标准 .NET 事件模式​ 使用泛型版本的标准 .NET 事件模式​ 补充总结 参考前言 前面介绍了C#中的委托&#xff0c;事件的很多部分都与委托…

问题 E: 便利店

题目描述 天宝来到便利店想买些饮料。便利店有各种型号的瓶装饮料售卖&#xff0c;不同型号的饮料卖不同的价格。1瓶0.25升的卖A元&#xff0c;1瓶0.5升的饮料卖B元&#xff0c;1瓶1升的卖C元&#xff0c;1瓶2升的卖D元。便利店里每种饮料都是无限供应。 天宝要买N升的饮料&a…

Beauty algorithm(一) 关键点检测

在实现美妆算法过程中&#xff0c;需要定位到目标区域&#xff0c;常规的图像处理技术很难准确定位。随着AI技术快速发展&#xff0c;人脸关键点成为面部特征提取技术之一。 常见开源的关键点有dlib、mtcnn等。 由于本人对dlib有所了解&#xff0c;且支持C、python等。故本人选…

【深度学习】SDXL tensorRT 推理

stabilityai/stable-diffusion-xl-1.0-tensorrt 项目&#xff1a;https://huggingface.co/stabilityai/stable-diffusion-xl-1.0-tensorrt TensorRT环境&#xff1a; git clone https://github.com/rajeevsrao/TensorRT.git cd TensorRT git checkout release/9.2stabilitya…

智云影院CMS程序PHP源码V3.0 无需数据库

本程序无需数据库&#xff0c;直接上传源码即可访问&#xff0c;&#xff08;服务器或虚拟主机空间&#xff09;都可以搭建使用&#xff01;模板自适应端&#xff0c;浏览体验更佳&#xff01;安装操作简单&#xff01;无需繁琐的操作&#xff0c;即可快速拥有一个视频看片资源…

DMX512输出协议详解

目录 ​编辑 1、DMX512协议简介 2、DMX512协议分析 DMX512指令帧介绍 DMX512信息包 3、DMX512接口电路 4、参考代码 1、DMX512协议简介 DMX512是一种用于舞台灯光控制的数字传输协议。它是由美国舞台灯光协会&#xff08;USITT&#xff09;于1990年发布的工业标准&…

uniapp 日历组件

我们的需求是显示当前月和下个月的排班表 引入 uniapp 日历组件 uni-calendar 做法有两种&#xff0c;一种是直接去修改组件&#xff0c;还有就是文档中提供的 selected 方法 修改组件的就不写了 <uni-calendar :lunar"true" :selected"selected" :in…

助力实体店数字化升级,VR智慧门店打造线上逛店体验

近年来&#xff0c;传统实体店业绩增长过于缓慢&#xff0c;实体门店的销售疲态十分明显&#xff0c;甚至于部分城市已经出现大量线下实体店开始关门的现象&#xff0c;因此顺应实体零售数字化升级趋势已经刻不容缓。越来越多的实体门店开始意识到这个问题&#xff0c;并逐步开…

日程安排小程序实战教程

日常中我们经常有一些事情需要提醒自己&#xff0c;使用日历的形式比较符合实际的使用习惯。本篇我们就利用微搭低代码工具带着大家开发一款日程安排的小程序。 1 创建数据源 登录微搭低代码控制台&#xff0c;打开数据模型&#xff0c;点击创建 输入数据源的名称日程安排 …

MySQL进阶篇(二) 索引

一、索引概述 1. 介绍 索引&#xff08;index&#xff09;是帮助 MySQL 高效获取数据 的 数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数…

SpringBoot内嵌的Tomcat启动过程以及请求

1.springboot内嵌的tomcat的pom坐标 启动后可以看到tomcat版本为9.0.46 2.springboot 内嵌tomcat启动流程 点击进入SpringApplication.run()方法里面 看这次tomcat启动相关的核心代码refreshContext(context);刷新上下文方法 public ConfigurableApplicationContext run(Stri…