這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 引言 在日常前端開發中,我們經常會面對一個讓人頭疼的問題:按鈕被用戶點擊了兩次以上,導致出現重覆提交表單或者發送重覆的請求。這個問題常見而且惱人。為瞭解決這個問題,我們需要一個又簡單又實用的方法,可以在不搞亂原有代碼的情況下,有效地防止按 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
引言
在日常前端開發中,我們經常會面對一個讓人頭疼的問題:按鈕被用戶點擊了兩次以上,導致出現重覆提交表單或者發送重覆的請求。這個問題常見而且惱人。為瞭解決這個問題,我們需要一個又簡單又實用的方法,可以在不搞亂原有代碼的情況下,有效地防止按鈕被連續點擊。
背景
隨著網頁應用變得越來越複雜,用戶在頁面上的交互也變得越來越頻繁。這就使得按鈕被不小心點擊多次的情況變得非常普遍。一般的解決方法存在一些問題,比如改動原有代碼太多,不夠靈活等。因此,我們需要一種更好的、通用的按鈕防連點方法。
挑戰
在解決按鈕被連點的問題時,我們要面臨一些挑戰。首先,解決方法得適應各種情況,比如表單提交、非同步請求等。其次,我們需要確保解決方法不會讓我們的原有代碼變得混亂,同時還要具備足夠的靈活性。最後,我們希望不修改原有代碼的情況下提供按鈕防連點。
目標
本文的目標是為大家提供一個簡單易用的按鈕防連點解決方案。我們會深入講解方案的設計原理和實現細節,並且會附上完整的源碼解析。通過學習本文,你將能夠理解這個解決方案的原理,同時學會如何在實際項目中應用這個方法。希望通過這篇文章,按鈕防連點問題不再讓你感到頭疼,反而變得得心應手。
防連點原理概述
在開始實現我們的按鈕防連點終極解決方案之前,讓我們首先理解一下連點問題的本質以及為什麼傳統的解決方案可能存在一些問題。
連點問題的本質
按鈕連點問題的核心在於,用戶在短時間內多次點擊按鈕,導致觸發相同的操作。這可能引發一系列不良後果,比如重覆提交表單、重覆發送請求等。為瞭解決這個問題,我們需要一種機制來在用戶點擊按鈕後一段時間內禁用按鈕,防止其再次觸發相同的操作。
常規解決方案的局限性
傳統的解決方案往往通過在點擊按鈕後添加禁用狀態,然後在一段時間後再啟用按鈕,來防止連點問題。然而,這種方法存在一些局限性。首先,它可能需要修改原有的按鈕組件,使得在多個地方應用時不夠靈活。其次,由於採用了定時器等機制,可能導致在某些情況下並不准確,或者在非同步操作中存在問題。
給按鈕加指令
創建一個可復用的Vue自定義指令,該指令能夠動態地管理按鈕的狀態,以防止用戶在短時間內多次點擊按鈕。關鍵之處在於,我們還將支持外部傳遞參數,以自定義按鈕的禁用時間。
創建可復用的防連點按鈕組件
首先,我們需要創建一個按鈕組件,該組件可以接受我們的自定義指令。這樣,我們就可以在需要的按鈕上應用這個指令。
<template> <button v-prevent-duplicate-clicks="2000" @click="handleClick">防連點按鈕</button> </template> <script> import preventDuplicateClicks from '@/path-to-your-file/preventDuplicateClicks'; export default { directives: { preventDuplicateClicks, }, methods: { handleClick() { // 處理按鈕點擊事件的業務邏輯 }, }, }; </script>
在上述代碼中,我們創建了一個按鈕組件,通過 v-prevent-duplicate-clicks
指令來防止按鈕的連點行為。並且,我們通過傳遞參數 "2000"
指定了按鈕禁用的時間為2秒。
指令文件
現在,創建一個名為 preventDuplicateClicks.js
的文件,該文件包含我們的自定義指令。
// preventDuplicateClicks.js const preventDuplicateClicks = { mounted(el, binding) { const { value } = binding; el.addEventListener('click', () => { if (!el.disabled) { el.disabled = true; setTimeout(() => { el.disabled = false; }, value || 1000); // 預設1秒後恢復按鈕點擊 } }); }, }; export default preventDuplicateClicks;
在上述代碼中,我們定義了一個名為 preventDuplicateClicks
的自定義指令,它在按鈕被點擊時阻止多次點擊。通過 setTimeout
來實現按鈕在一定時間後恢復點擊。此外,我們在指令上支持了外部參數的傳遞,用於自定義按鈕的禁用時間。
以為估計是80%的前端的解決方案,我在面試中也問過類似的問題,很多人能給出指令的寫法已經很好了。有如下問題:
- 侵入已有代碼:如果是老的項目,都要添加這個功能,要去批量改,很麻煩;
- 禁用時間錯誤:我們假設給的參數是2秒,一個介面請求超過2秒, 用戶在請求響應之前依然會重覆發起請求。如果介面幾十毫秒就返回(是異常,讓重試),用戶也得等2秒;
那麼有沒有更好的解決方案了,肯定是有的,我們來分析以上兩個問題:
- 侵入代碼:我們通過重寫已有組件可以做到,假設用的el-button,我們在全局把el-button覆蓋成我們自己的即可
- 禁用時間錯誤:既然設置是錯的,那我們就不設置,按鈕肯定都有一個onClick的函數,我們只要在onClick執行之前設置禁用,執行後啟用即可。
終極解決方案
有了以上的分析,我們直接貼代碼吧,我司用的vue + ant design vue,其他組件庫,類似寫法吧。
<script> import { Button as AntButton } from 'ant-design-vue' export default { name: 'AButton', props: { ...AntButton.props, delay: { type: Number, default: 300 } }, data() { return { customLoading: false, ownDisabled: false } }, computed: { allProps() { return Object.assign({}, this.$props, { loading: this.customLoading || this.loading, type: this.$props.type || 'primary' }) } }, methods: { async handler (...arg) { if (this.ownDisabled) return this.customLoading = true this.ownDisabled = true const { click: preClick } = this.$listeners || {} const ret = preClick(...arg) try { await Promise.resolve(ret) } finally { this.customLoading = false let timer = setTimeout(() => { this.ownDisabled = false clearTimeout(timer) timer = null }, this.delay) } } }, render() { return ( <AntButton props={this.allProps} onClick={this.handler}> {this.$slots?.default} </AntButton> ) } } </script>需要註意的是,使用的時候
onClick
需要返回promise,這算一個很容易遵守的約定吧。另外加個預設延遲300ms,目的我記得好像是為了避免路由跳轉時的問題,大家可以自己調整。