时间:2022-03-20 09:49:33 | 栏目:vue | 点击:次
相信各位在开发中一定遇到过二次弹框确认相关的需求。不管你使用的是UI框架的二次弹框组件,还是自己封装的弹框组件。都避免不了在多次使用时出现大量重复代码的问题。这些代码的积累导致项目的可读性差。项目的代码质量也变得很差。那么我们如何解决二次弹框代码重复的问题呢?使用装饰器
Decorator是ES7的一个新语法。Decorator通过对类、对象、方法、属性进行修饰。对其添加一些其他的行为。通俗来说:就是对一段代码进行二次包装。
使用方法很简单 我们定义一个函数
const decorator = (target, name, descriptor) => { var oldValue = descriptor.value; descriptor.value = function(){ alert('哈哈') return oldValue.apply(this,agruments) } return descriptor } // 然后直接@decorator到函数、类或者对象上即可。
装饰器的目的旨在对代码进行复用。下面我们先来一个小例子看看
//定义一个装饰器 const log = (target, name, descriptor) => { var oldValue = descriptor.value; descriptor.value = function() { console.log(`Calling ${name} with`, arguments); return oldValue.apply(this, arguments); }; return descriptor; } //计算类 class Calculate { //使用装饰器 @log() function subtraction(a,b){ return a - b } } const operate = new Calculate() operate.subtraction(5,2)
const log = (func) => { if(typeof(func) !== 'function') { throw new Error(`the param must be a function`); } return (...arguments) => { console.info(`${func.name} invoke with ${arguments.join(',')}`); func(...arguments); } } const subtraction = (a, b) => a + b; const subtractionLog = log(subtraction); subtractionLog(10,3);
这样一对比你会发现使用装饰器后代码的可读性变强了。装饰器并不关心你内部代码的实现。
如果你的项目是用vue-cli搭建的 并且vue-cli的版本大于2.5 那么你无需进行任何配置即可使用。如果你的项目还包含eslit 那么你需要在eslit中开启支持装饰器相关的语法检测
//在 eslintignore中添加或者修改如下代码: parserOptions: { ecmaFeatures:{ // 支持装饰器 legacyDecorators: true } }
加上这段代码之后eslit就支持装饰器语法了。
通常在项目中我们经常会使用二次弹框进行删除操作:
//decorator.js //假设项目中已经安装了 element-ui import { MessageBox, Message } from 'element-ui' /** * 确认框 * @param {String} title - 标题 * @param {String} content - 内容 * @param {String} confirmButtonText - 确认按钮名称 * @param {Function} callback - 确认按钮名称 * @returns **/ export function confirm(title, content, confirmButtonText = '确定') { return function(target, name, descriptor) { const originValue = descriptor.value descriptor.value = function(...args) { MessageBox.confirm(content, title, { dangerouslyUseHTMLString: true, distinguishCancelAndClose: true, confirmButtonText: confirmButtonText }).then(originValue.bind(this, ...args)).catch(error => { if (error === 'close' || error === 'cancel') { Message.info('用户取消操作')) } else { Message.info(error) } }) } return descriptor } }
如上代码 confirm方法里执行了一个element-ui中的MessageBox组件 当用户取消时 Message组件会提示用户取消了操作。
我们在test()方法上用装饰器修饰一下
import { confirm } from '@/util/decorator' import axios form 'axios' export default { name:'test', data(){ return { delList: '/merchant/storeList/commitStore' } } }, methods:{ @confirm('删除门店','请确认是否删除门店?') test(id){ const {res,data} = axios.post(this.delList,{id}) if(res.rspCd + '' === '00000') this.$message.info('操作成功!') } }
此时用户点击某个门店进行删除。装饰器将会起作用。弹出如下图所示:
当我点击取消时:
tips: 用户取消了操作.被修饰的test方法不会执行。
当我们点击确定时:
接口被调用了 并且弹出了message
下面小编罗列了几个小编在项目中常用的几个装饰器,方便大家使用
函数节流与防抖应用场景是比较广的,一般使用时候会通过throttle或debounce方法对要调用的函数进行包装,现在就可以使用上文说的内容将这两个函数封装成装饰器, 防抖节流使用的是lodash提供的方法,大家也可以自行实现节流防抖函数哦
import { throttle, debounce } from 'lodash' /** * 函数节流装饰器 * @param {number} wait 节流的毫秒 * @param {Object} options 节流选项对象 * [options.leading=true] (boolean): 指定调用在节流开始前。 * [options.trailing=true] (boolean): 指定调用在节流结束后。 */ export const throttle = function(wait, options = {}) { return function(target, name, descriptor) { descriptor.value = throttle(descriptor.value, wait, options) } } /** * 函数防抖装饰器 * @param {number} wait 需要延迟的毫秒数。 * @param {Object} options 选项对象 * [options.leading=false] (boolean): 指定在延迟开始前调用。 * [options.maxWait] (number): 设置 func 允许被延迟的最大值。 * [options.trailing=true] (boolean): 指定在延迟结束后调用。 */ export const debounce = function(wait, options = {}) { return function(target, name, descriptor) { descriptor.value = debounce(descriptor.value, wait, options) } }
封装完之后,在组件中使用
import {debounce} from '@/decorator' export default { methods:{ @debounce(100) resize(){} } }
在加载数据的时候,为了个用户一个友好的提示,同时防止用户继续操作,一般会在请求前显示一个loading,然后在请求结束之后关掉loading,一般写法如下
export default { methods:{ async getData() { const loading = Toast.loading() try{ const data = await loadData() // 其他操作 }catch(error){ // 异常处理 Toast.fail('加载失败'); }finally{ loading.clear() } } } }
我们可以把上面的loading的逻辑使用装饰器重新封装,如下代码
import { Toast } from 'vant' /** * loading 装饰器 * @param {*} message 提示信息 * @param {function} errorFn 异常处理逻辑 */ export const loading = function(message = '加载中...', errorFn = function() {}) { return function(target, name, descriptor) { const fn = descriptor.value descriptor.value = async function(...rest) { const loading = Toast.loading({ message: message, forbidClick: true }) try { return await fn.call(this, ...rest) } catch (error) { // 在调用失败,且用户自定义失败的回调函数时,则执行 errorFn && errorFn.call(this, error, ...rest) console.error(error) } finally { loading.clear() } } } }
然后改造上面的组件代码
export default { methods:{ @loading('加载中') async getData() { try{ const data = await loadData() // 其他操作 }catch(error){ // 异常处理 Toast.fail('加载失败'); } } } }
当你点击删除按钮的时候,一般都需要弹出一个提示框让用户确认是否删除,这时候常规写法可能是这样的
import { Dialog } from 'vant' export default { methods: { deleteData() { Dialog.confirm({ title: '提示', message: '确定要删除数据,此操作不可回退。' }).then(() => { console.log('在这里做删除操作') }) } } }
我们可以把上面确认的过程提出来做成装饰器,如下代码
import { Dialog } from 'vant' /** * 确认提示框装饰器 * @param {*} message 提示信息 * @param {*} title 标题 * @param {*} cancelFn 取消回调函数 */ export function confirm( message = '确定要删除数据,此操作不可回退。', title = '提示', cancelFn = function() {} ) { return function(target, name, descriptor) { const originFn = descriptor.value descriptor.value = async function(...rest) { try { await Dialog.confirm({ message, title: title }) originFn.apply(this, rest) } catch (error) { cancelFn && cancelFn(error) } } } }
然后再使用确认框的时候,就可以这样使用了
export default { methods: { // 可以不传参,使用默认参数 @confirm() deleteData() { console.log('在这里做删除操作') } } }
是不是瞬间简单多了,当然还可以继续封装很多很多的装饰器
装饰器用于将一段代码进行二次包装。给代码增加一些行为操作和属性。 使用装饰器能大大减少代码的重复。增强代码可读性。