導語:隨著接觸的專案增加,很多專案都是遇到同樣的問題,而每次都是使用一貫的手法進行處理。有時候有些方法並不是那麼的優雅甚至有些冗餘,所以自己也想開始嘗試不同的方法去解決同樣的問題。
我經常在專案中會遇到按鈕重複點選後引起表單的重複點選問題。所以針對這個問題,自己嘗試了幾種辦法分別去解決。直接上程式碼。
1.粗暴簡單辦法
直接定義一個變數,每次點選過後等所有操作結束後釋放變數。或使用loading防止使用者點選
//* 部分程式碼
<script>
export default {
methods: {
onSubmit() {
if (this.lock) return;
this.lock = true;
// const load = this.$loading();
this.$http.create().then((res) => {
// do something
this.lock = false;
// load.close();
}).catch(() => {
this.lock = false;
// load.close();
})
},
},
}
</script>
複製程式碼
這種辦法簡單粗暴,但是每次需要防止重複點選的地方,都要去關注lock或者loading的重置,總覺的很囉嗦。也沒辦法好好的抽離出來。(PS:能力有限,自己也沒有想到比較好的辦法在上層優雅的封裝出來)
2.直接把loading放到http請求中去做,統一封裝方法
//* 部分程式碼
...
let load;
http.interceptors.request.use((config) => {
load = Loading();
...
return config;
}, error => {
load.close();
return Promise.reject(error)
});
http.interceptors.response.use((response) => {
load.close();
...
return response;
},error => {
load.close();
return Promise.reject(error);
});
複製程式碼
這種辦法在實際中也用過了一段時間,一開始挺好用的,但是在後面自己弱網測試的時候發現也是會導致重複點選的情況。而且在有些時候loading圖並不是所有請求都需要,還要去做個是否顯示loading的配置,這樣感覺http請求又笨重了,也沒有讓重複點選功能抽離出來。
3.裝飾器方法
說到裝飾器,最經典的應用場景就是面向切片程式設計(AOP),《前端常用設計模式(1)--裝飾器(decorator)》juejin.im/post/5cb415… 做出了很棒的理解與應用。得益於ES7和TS,裝飾器在Angular和react中都有很多案例,因為Vue中Class不是必選,所以在Vue中很少看到使用裝飾器的,得益於官方有vue-class-component來使用Class進行建立元件的方法,開始了自己的嘗試之路。
lock.js
export function lock(target, key, desc) {
const fn = desc.value;
//* 沒有使用箭頭函式是為了讓this能指回到vue,這樣就可以獲取到vue的data,從而做更多的事情,下面會講到
desc.value = async function() {
if (this.$lock) return;
this.$lock = true;
/**
* await fn.apply(this).catch(() => {
* this.$lock = false;
* });
* this.$lock = false;
*/
await fn.apply(this).finally(() => {
this.$lock = false;
})
return target;
};
}
複製程式碼
index.vue
<template>
<!-- do something -->
</template>
<script>
import Vue from 'vue';
import Component from 'vue-class-component';
import { lock } from './lock';
@Component
export default class extends Vue {
@lock
async onSubmit() {
await this.$http.create();
// do something
}
}
</script>
複製程式碼
感覺這樣就完全抽離了重複點選的功能(PS:好像是這樣的),也能獨立測試,想在哪裡用就在哪裡用。感覺不足的是,裝飾器裡需要讓this重新指回vue才能獲取到vue的data
4.舉一反三
既然重複點選可以從業務程式碼中抽離出來,那我們提交表單的欄位驗證也就同樣可以抽離出來了。(PS:所有UI框架都有成熟的form表單驗證元件,就當我是瞎折騰)
validate.js
export function validate(target, key, desc) {
const fn = desc.value;
desc.value = async function () {
const {
name, phone,
} = this.data;
if (!name) {
return confirm('請輸入您的姓名');
}
if (name.length > 20) {
return confirm('您的姓名不能超過20個字');
}
if (!phone) {
return confirm('請輸入您的電話');
}
if (!((/^\d{11}$/.test(phone)))) {
return confirm('請輸入11位的電話號碼');
}
await fn.apply(this);
return target;
};
}
複製程式碼
index.vue
<template>
<!-- do something -->
</template>
<script>
import Vue from 'vue';
import Component from 'vue-class-component';
import { validate } from './validate';
@Component
export default class extends Vue {
data = {
name: '',
phone: '',
}
@validate
async onSubmit() {
await this.$http.create();
// do something
}
}
</script>
複製程式碼
5.防抖方法(補充)
有小夥伴說可以使用防抖,個人覺得還是需要看場景,這裡也就列出防抖的方法。
防抖方法是一個很好限制重複事件頻繁觸發的,經常用在scroll、resize事件上,也可以嘗試用在重複點選上面。但是如果點選事件後需要有非同步處理,單單使用防抖方法也會沒辦法限制弱網(PS:吐槽一下成都地鐵上移動經常網路不好)下重複點選的情況。如:防抖時間為1秒,但是請求花掉了2秒才返回資料給前端進行處理,中間產生了時間差,導致使用者有時間重複點選。所以個人覺得還是需要配合其它辦法。同樣列出防抖的列子:
throttle.js
const throttle = function(fn, wait, scope) {
clearTimeout(throttle.timer);
throttle.timer = setTimeout(function() {
fn.apply(scope);
}, wait);
};
複製程式碼
index.vue
<template>
<!-- do something -->
</template>
<script>
export default {
onSubmit() {
throttle(() => {
this.$http.create().then((result) => {
// do something
});
}, 1000);
},
};
</script>
複製程式碼
小結:
本文主要是讓自己,通過某個功能對自己程式碼進行review和重實現來提高程式碼質量,希望各位大佬多多指點。下面是我的微訊號,希望和大家多多交流學習。(新增微信請註明來意謝謝!)