当前位置: 首页 > 科技观察

vue秀技巧,策略模式实现动态表单验证

时间:2023-03-11 23:46:39 科技观察

本文转载自微信公众号《前端下午茶》,作者SHERlocked93。转载本文请联系前端下午茶公众号。StrategyPattern,又称为PolicyPattern,定义了一系列算法,将它们一个一个封装起来,并使它们可以互换。封装的策略算法一般是独立的,策略模式根据输入调整使用哪种算法。关键是政策执行和使用的分离。注意:本文可能会用到IIFE(ImmediatelyInvokedFunctionExpression,立即调用函数表达式)、ES6语法let/const、箭头函数、rest参数、短路运算符等一些编码技巧,如果你没接触过然而,你可以点击链接一点点学习~1。你以前见过的策略模式。现在的电子产品种类繁多,尺寸也各不相同。device),但是螺丝规格很多,螺丝刀的规格也很多。如果每次遇到规格都去买螺丝刀,家里就得堆螺丝刀了。所以现在人们使用多功能起子套装,只需要一个起子柄,遇到不同规格的螺丝就可以更换起子头,非常方便,体积也小了很多。再举个例子,一辆汽车有很多种轮胎。多走泥泞路可以用泥地胎,多走雪路就用雪地胎,多走高速公路就用高速胎。性能轮胎可以针对不同的使用场景更换不同的轮胎,而无需更换整车。这些都是策略模式的例子。螺丝刀/汽车属于封装上下文,不同的螺丝刀头/轮胎被封装使用。这里的螺丝刀头/轮胎相当于策略,可以根据不同的需要更换不同的使用策略。在这些场景中,有以下特点:螺丝刀头/轮胎(策略)相互独立,但可以相互替换;screwdriver/car(封装上下文)可以根据不同的需求选择不同的策略;2.实例代码实现对于具体的实例,我们使用编程实例来演示,更容易量化。场景是这样的。某电商网站想举办一场活动,通过打折促销的方式销售库存商品。十一统治恐惧),这个逻辑交给我们,我们怎么实现。functionpriceCalculate(discountType,price){if(discountType=='minus100_30'){//满100减30returnprice-Math.floor(price/100)*30}elseif(discountType==='minus200_80'){//full200minus80returnprice-Math.floor(price/200)*80}elseif(discountType==='percent80'){//20%offreturnprice*0.8}}priceCalculate('minus100_30',270)//输出:210priceCalculate('percent80',250)//Output:200通过判断输入的折扣类型来计算产品的总价,几个if-else可以满足需求,但是这种方式的缺点也很明显:priceCalculatefunction随着打折类型的增加,if-else判断语句会越来越臃肿;如果增加了新的折扣类型或者更改了折扣类型的算法,则需要更改priceCalculate函数的实现,这违反了开闭原则;复用性差。如果其他地方也有类似的算法,只是规则不一样,上面的代码就不能复用了;我们可以修改它来提取计算折扣的算法并将其保存为一个对象,并使用折扣的类型作为键。这样在索引时通过对象的key-value索引调用具体的算法:constDiscountMap={minus100_30:function(price){returnprice-Math.floor(price/100)*30},minus200_80:function(price){returnprice-Math.floor(price/200)*80},percent80:function(price){returnprice*0.8}}/*计算总售价*/functionpriceCalculate(discountType,price){returnDiscountMap[discountType]&&DiscountMap[discountType](price)}priceCalculusate('minus100_30',270)priceCalculate('percent80',250)//Output:210//Output:200这样就把算法的实现和算法的使用分开了,变得很简单添加一个新的算法:DiscountMap.minus150_40=function(price){returnprice-Math.floor(price/150)*40}如果你想隐藏计算算法,可以借助IIFE使用闭包方法。这时候需要增加一个入口来增加策略方便扩展:constPriceCalculate=(function(){/*价格计算方法*/constDiscountMap={minus100_30:function(price){//full100minus30returnprice-Math.floor(price/100)*30},minus200_80:function(price){//满200减80returnprice-Math.floor(price/200)*80},percent80:function(price){//20%offreturnprice*0.8}}return{priceClac:function(discountType,price){returnDiscountMap[discountType]&&DiscountMap[discountType](price)},addStrategy:function(discountType,fn){//注册新的计算方法if(DiscountMap[discountType])returnDiscountMap[discountType]=fn}}})()PriceCalculate.priceClac('minus100_30',270)//输出:210PriceCalculate.addStrategy('minus150_40',function(price){returnprice-Math.floor(price/150)*40})PriceCalculate.priceClac('minus150_40',270)//输出:230这样算法就隐藏起来了,预留一个增加策略的入口,方便扩展3.策略模式的一般实现根据上面的例子,可以细化策略模式。折扣的计算方法可以看成是一种策略(Strategy),这些策略之间可以相互替换,而具体的折扣计算过程可以看成是封装上下文(Context),封装上下文可以根据需要选择不同的策略需要。主要有以下几个概念:Context:封装上下文,根据需要调用需要的策略,屏蔽外界直接调用策略,对外只提供一个接口,根据需要调用相应的策略;Strategy:策略,包含具体的算法,其方法外观相同,因此可以相互替换;StrategyMap:封装上下文中调用的所有策略的集合;结构图如下:用一个泛化的方法来实现策略模式。constStrategyMap={}functioncontext(type,...rest){returnStrategyMap[type]&&StrategyMap[type](...rest)}StrategyMap.minus100_30=function(price){returnprice-Math.floor(price/100)*30}context('minus100_30',270)//输出:210的大体实现貌似比较简单,这里是项目实战。4.实战中的策略模式4.1Tableformatter下面是在Vue+ElementUI项目中使用的一个例子。其他框架的工程原理大同小异,分享给大家。Element的表格控件的Column接受一个formatter参数,用来格式化内容,它的类型是function,也可以接受几个具体的参数,像这样:Function(row,column,cellValue,index)。以文件大小转换为例,后端往往直接以bit为单位传输文件大小,因此前端需要根据后端数据将文件大小转换为自己需要的单位,如KB/MB。首先实现文件计算的算法:exportconstStrategyMap={/*Strategy1:将文件大小(bit)转换为KB*/bitToKB:val=>{constnum=Number(val)returnisNaN(num)?val:(num/1024)。toFixed(0)+'KB'},/*策略2:将文件大小(位)转换为MB*/bitToMB:val=>{constnum=Number(val)returnisNaN(num)?val:(num/1024/1024).toFixed(1)+'MB'}}/*上下文:生成el格式格式化程序*/conststrategyContext=function(type,rowKey){returnfunction(row,column,cellValue,index){returnStrategyMap[type](row[rowKey])}}exportdefaultstrategyContext那么在组件中我们可以直接:代码示例可以在codepen中找到——strategy模式实际运行结果如下:4.2FormVerification除了form中的formatter,在formverification场景中也经常用到strategy模式。这是Vue+ElementUI项目的示例。其他框架类似。ElementUI的Form表单具有表单验证功能。用于验证用户输入的表单内容。在实际需求中,表单验证项一般比较复杂,因此需要为每个表单项添加一个validator自定义验证方法。我们可以像官网例子一样,把表单验证写在组件的状态数据函数中,但是表单验证方法复用频率高,不容易复用。这时候我们可以结合策略模式和函数柯里化的知识来重构一下。首先,我们在项目的工具模块(通常是utils文件夹)中实现一个常用的表单验证方法://src/utils/validates.js/*Namevalidationconsistsof2-10Chinesecharacters*/exportfunctionvalidateUsername(str){constreg=/^[\u4e00-\u9fa5]{2,10}$/returnreg.test(str)}/*手机号验证由11位数字组成*/exportfunctionvalidateMobile(str){constreg=/^1\d{10}$/returnreg.test(str)}/*电子邮件验证*/exportfunctionvalidateEmail(str){constreg=/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/returnreg.test(str)}然后在utils/index.js中添加柯里化方法生成表单验证函数://src/utils/index.jsimport*asValidatesfrom'./validates.js'/*生成表单自定义验证函数*/exportconstformValidateGene=(key,msg)=>(rule,value,cb)=>{if(Validates[key](value)){cb()}else{cb(newError(msg))}}上面的formValidateGene函数接受两个参数,第一个是验证规则,也就是src/utils/validates.jsT从文件中提取的通用校验规则的方法名,第二个参数为表单校验报错时的提示信息。可见使用起来还是很方便的。将表单验证方式提取为策略,通过currying方法动态选择表单验证方式,使策略使用灵活,大大加快开发效率的代码示例,请参考codesandbox-策略模式表单验证。实际结果:5.策略模式的优缺点策略模式拆分了算法的实现和使用。这个特点带来很多好处:策略是相互独立的,但是策略可以自由切换。这种策略模式的特点给策略模式带来了很大的灵活性,也提高了策略的复用率;如果不使用策略模式,那么在选择策略的时候一般会使用多重条件判断,采用策略模式可以避免多重条件判断,增加可维护性;可扩展性好,策略易于扩展;策略模式的缺点:策略之间是相互独立的,所以一些复杂的算法逻辑无法共享,造成了一定的资源浪费;如果用户要采用任何策略,就必须了解该策略的实现,所以所有的策略都需要对外暴露,这就违反了得墨忒耳法则/最少知识原则,同时也增加了使用该策略的成本用户的策略对象。6.策略模式的适用场景那么在什么场景下应该使用策略模式呢:在多个算法只是行为略有不同的场景下,可以使用策略模式来动态选择算法;在算法需要自由切换的场景下;有时需要多次条件判断,可以使用策略模式来避免多次条件判断;7.其他相关模式7.1策略模式和模板方法模式策略模式和模板方法模式的作用类似,只是结构和实现有点不同。策略模式允许我们在程序运行时动态指定要使用的算法;模板方法模式决定了定义子类时使用的算法;7.2策略模式和享元模式见享元模式中的介绍。https://mp.weixin.qq.com/s/2B3OVfvGkbbpzCFKVc6zVw