vue實現檢測敏感詞過濾元件的多種思路
寫在前面
在做商戶端敏感詞檢測的過程中,發現了一些問題,特在此總結。本文的行文思路是編寫敏感詞檢測程式碼,前兩個思路未採用元件化的開發思想,後三個思路根據需求變更,採用元件化的思想和mixins混入進行開發。
需求分析v1
在商戶端產品模組,點選產品編輯按鈕,可以修改產品的標題和價格。當沒有填寫產品標題時,置灰儲存按鈕;當填寫的產品標題屬於敏感詞時,置灰儲存按鈕並給出後端返回的提示。
在商戶端定製模組,定製完成後,直接跳轉到產品編輯頁面,可以修改產品標題和價格。當沒有填寫產品標題時,置灰儲存按鈕;當填寫的產品標題屬於敏感詞時,置灰儲存按鈕並給出後端返回的提示。
思路一:使用截流方法監聽輸入框的input事件
因為本人負責的是產品模組,不知道定製模組的產品標題也需要增加敏感詞檢測,所以最開始並沒有將敏感詞檢測寫成元件。於是有了第一種思路:使用截流方法監聽輸入框的input事件。
思路:
1.只要輸入不為空或者全是空格,每次輸入後都會觸發輸入框的input事件,呼叫敏感詞檢測介面。頻繁地請求介面會給伺服器造成巨大的壓力,因此可以使用截流方法降低介面請求的頻率;
2.為請求的介面增加一個定時器,並新增一個倒計時間,記為1s,每次輸入後都延遲1s請求介面。於是可以在輸入框的input事件觸發後,清除掉上一個定時器。防止定時器疊加,重複請求多次介面,只保留最後一次input事件觸發的敏感詞檢測的介面請求。這也就意味著,如果使用者連續輸入產品標題,且每次輸入的間隔時間小於1s,則使用者最後一次輸入的資訊會被介面檢測是否合乎敏感詞規範;如果使用者間隔著輸入產品標題,且間隔時間都超過1s,則會發起多次介面請求,還是會對伺服器造成不小的壓力,所以這種方法還是存在侷限性。
//敏感詞檢測的html <div class="edit-title">產品標題</div> <el-input v-model="productName" placeholder="請輸入產品標題" type="text" auto-complete="on" clearable @input="inspectSpams" /> <div v-if="showMessage" class="message">{{ errorMessage }}</div> //儲存按鈕的html <el-button type="primary" @click="save" :disabled="productName === '' || showMessage === true">儲存</el-button>
data() { return { productName: '',errorMessage: '',showMessage: false,timer: -1 } },methods: { inspect() { if(this.productName.trim() !== '') { this.$store.dispatch('products/inspectSpams',this.productName).catch((err)=>{ this.errorMessage = err.response.data.message this.showMessage = true }) } },inspectSpams() { this.showMessage = false clearTimeout(this.timer) this.timer = setTimeout(() => { this.inspect() },1000) } }
缺陷: 當為產品標題新增敏感詞後,只要手速足夠快,在1s內點選儲存按鈕,還是可以成功儲存敏感詞,這與我們的需求相違背。
思路二:使用輸入框的失焦和儲存按鈕的點選事件
既然使用輸入框的input事件存在問題,那是否能夠使用輸入框的失焦事件和儲存按鈕的點選事件呢?答案是可以的。只不過在這個過程中,有兩個問題需要特別注意。
注意事項:
- 需要理解失焦事件。 何為失焦?在輸入框內輸入完標題之後,點選輸入框外的任意地方都會觸發失焦事件。所以,千萬不要忘記點選儲存按鈕同樣會觸發失焦事件。因此,只需要在輸入框失焦事件中請求敏感詞檢測介面,無需在點選事件中重複請求。
- 需要考慮觸發失焦事件和點選事件帶來的的非同步問題。 點選儲存按鈕會同時觸發失焦事件和點選事件,失焦事件會優先點選事件執行。失焦事件用於請求敏感詞檢測介面,點選事件用於修改產品資訊。互動邏輯是先請求敏感詞檢測介面,如果介面返回的狀態是成功,則不需要顯示錯誤提示資訊;否則需要顯示後端返回的錯誤資訊,並禁用儲存按鈕。待校驗成功之後再請求修改產品資訊的介面。因此,這兩個方法是存在一個先後順序的。而且一定是失焦事件請求敏感詞介面在前,請求修改產品資訊在敏感詞介面請求結束之後。不能因為敏感詞檢測過慢導致非法的敏感詞已經成功儲存並渲染,此時再請求敏感詞檢測介面是沒必要的。由於無法確定和保證兩個介面的,所以需要增加一個變數來判斷敏感詞介面是否請求結束。如果介面沒有請求結束,需要在點選事件中重新發起請求;如果介面已經完成請求,則直接return忽略掉。
思路:
- 為輸入框新增失焦事件;
- 為儲存按鈕新增點選事件。
程式碼:
<div class="edit-title">產品標題</div> <el-input v-model="productName" placeholder="請輸入產品標題" type="text" auto-complete="on" clearable @blur="inspectSpams" /> <div v-if="showMessage" class程式設計客棧="message">{{ errorMessage }}</div> <el-button type="primary" @click="save" :disabled="!productName || showMessage">儲存</el-button>
data() { return { showMessage: false,productName: '',timer: -1,hasVerified: false } },methods: { //失焦事件(請求敏感詞檢測介面) async inspectSpams() { this.hasVerified = false this.showMessage = false if(this.productName.trim() !== '') { await this.$store.dispatch('products/inspectSpams',this.productName).catch((err) => { this.errorMessage = err.response.data.message this.showMessage = true }) } this.hasVerified = true }, //點選事件(請求修改產品資訊介面) async save() { if(!this.hasVerified) { await this.inspectSpams() } const variants = this.variants.map((variant) => { return { id: variant.id,price: variant.price,} }) const params = { variants,name: this.productName } params.productId = this.productId await this.$store.dispatch('products/editProduct',params) .then(async() => { await this.getProductListData(this.productStatus,1) this.$message({ www.cppcns.comtype: 'success',message: '產品修改成功!' }) }) .catch((message) => { this.$message({ type: 'error',message }) }) www.cppcns.com this.showEditProductDialog = false } }
思路三:使用mixins抽取敏感詞檢測方法
本以為敏感詞檢測就這樣離我而去了,結果收到產品訊息,同樣需要給定製模組的產品標題增添敏感詞檢測。之後就是一頓ctrl+c和ctrl+v操作猛如虎,最後發現程式碼冗餘過多,需要對程式碼進行整理。此處和後續都只寫產品模組的敏感詞檢測邏輯,定製模組的敏感詞檢測邏輯其實和產品模組大同小異。 既然定製模組的產品標題編輯和產品模組的樣式和邏輯相差無幾,那何不抽取敏感詞檢測公共方法呢?就這麼愉快地決定了,鍵盤上一頓狂敲亂打後,混入方法就成型了:
export default { data() { return { hasVerified: false,errorMessage: '' } },methods: { async inspectSpams(name) { this.hasVerified = false this.showMessage = false if(name.trim() !== '') { await this.$store.dispatch('products/inspectSpams',name).catch((err) => { this.errorMessage = err.response.data.message this.showMessage = true }) } this.hasVerified = true } } }
<div class="edit-title">產品標題</div> <el-input v-model="productName" placeholder="請輸入產品標題" type="text" auto-complete="on" clearable @blur="inspectSpams(productName)" /> <div v-if="showMessage" class="message">{{ errorMessage }}</div> <el-button type="primary" @click="save" :disabled="!productName || showMessage">儲存</el-button>
import inspectSpams from '@/mixins/inspectSpams' export default { data() { return { productName: '' } },mixins: [ inspectSpams ],methods: { //點選事件(請求修改產品資訊介面) async save() { if(!this.hasVerified) { await this.inspectSpams(this.productName) } const variants = this.variants.map((variant) => { return { id: variant.id,} }) const params = { variants,name: this.productName } params.productId = this.productId await this.$store.dispatch('products/editProduct',params) .then(async() => { await this.getProductListData(this.productStatus,1) this.$message({ type: 'success',message: '產品修改成功!' }) }) .catch((message) => { this.$message({ type: 'error',message }) }) this.showEditProductDialog = false } } }
思路四:使用promise封裝敏感詞檢測元件(面向需求v1)
考慮到產品模組敏感詞檢測和定製模組的html結構相差無幾,決定封裝一個敏感詞檢測元件,將產品標題敏感詞檢測的html程式碼和業務邏輯放在一起。但在這個過程中,有三個問題需要特別注意:
注意事項:
- async函式返回的是一個promise物件;
- 不能直接修改傳入的props值,但是可以通過中間變數,將中間變數和props雙向繫結,從而間接地修改props的值;
- 可以在父元件給props增加sync修飾符,在子元件中對props和中間變數進行監聽。如果任意一方的值發生變化,都會將改變後的值賦予給另一方,從而達到雙向繫結的目的。
思路:
- 編寫敏感詞檢測元件;
- 引入敏感詞檢測元件。
//敏感詞檢測元件 <template> <div> <el-input v-model="input" placeholder="請輸入產品標題" type="text" clearable @blur="inspectSpams" /> <div v-if="isShowMessage" class="message">{{ errorMessage }}</div> </div> </template> <script> export default { props: { title: { required: true,type: String } },data() { return { input: '',isShowMessage: false,hasVerified: true } },watch: { title: { handler(val) { this.input = val },immediate: true },input(val) { this.$emit('update:title',val) } },methods: { async inspectSpams() { this.hasVerified = false this.isShowMessage = false if (this.input !== '') { await this.$store.dispatch('products/inspectSpams',this.input).catch((err) => { this.errorMessage = err.response.data.message this.isShowMessage = true }) } this.hasVerified = true },init() { this.isShowMessage = false },async verify() { if (!this.hasVerified) { await this.inspectSpams() } const emptyInput = this.input.trim() if (emptyInput === '') { this.isShowMessage = true this.errorMessage = '請輸入產品名稱' } return new Promise((resvole,reject) => { if (Boolean(!emptyInput || this.isShowMessage)) { reject() } else { resvole() } }) } } } </script> <style> .message { font-weight: bold; color: red; margin-top: 10px; } </style>
//引入敏感詞檢測元件 <script> import { mapState } from 'vuex' import InspectSpams from '@/components/InspectSpams' export default { name: 'List',components: { InspectSpams },data() { return { productName: '' } },computed: { ...mapState({ variants: (state) => state.products.detail.variants }),},methods: { save() { this.$refs.productSpamsRef.verify() .then(async()=>{ const variants = this.variants.map((variant) => { return { id: variant.id,} }) const params = { variants,name: this.productName } params.productId = this.productId await this.$store.dispatch('products/editProduct',params) .then(async() => { await this.getProductListData(this.productStatus,1) this.$message({ type: 'success',message: '產品修改成功!' }) }) this.showEditProductDialog = false }) .catch(()=>{ this.$message({ type: 'error',message: '請輸入合法的產品名稱' }) }) },getProductListData(status,page) { this.$store.dispatch('products/getList',{ limit: 16,status,order: 'id',direction: 'desc',page }) } } </script> <template> <div> <div class="edit-title">產品標題</div> <InspectSpams :title.sync="productName" ref="productSpamsRef" /> <el-button type="primary" @click="save">儲存</el-button> </div> </template>
思路五:使用插槽和mixins封裝敏感詞檢測元件(面向需求v2)
需求變更: 其實相比需求v1來說,也沒發生什麼變化。只是產品模組的產品標題必須得填寫,不然得禁止儲存按鈕,而定製模組的產品標題可以不填寫,儲存後預設為填寫的產品標題 + 白板名稱。
思路: 既然如此,何不把給錯誤提示的html放入一個元件中,使用插槽佔位表示需要檢測的欄位,而將敏感詞檢測的邏輯放到mixins中。後續如果還有其它地方需要進行敏感詞檢測,會顯得更加靈活,其實這樣反倒更利於元件的複用原則。
//敏感詞檢測元件 <template> <div> <slot /> <div v-if="isShowMessage" class="message"> {{ errorMessage }} </div> </div> </template> <script> export default { props: { isShowMessage: { required: true,type: Boolean },errorMessage: { required: true,type: String } } } </script> <style> .message { font-weight: bold; color: red; margin-top: 10px; } </style>
//敏感詞檢測的mixins export default { data() { return { isShowMessage: false,hasVerified: true } },methods: { async inspectSpams(name) { this.hasVerified = false this.isShowMessage = false if (name.trim() !== '') { await this.$store.dispatch('products/inspectSpams',name).catch((err) => { this.errorMessage = err.response.data.message this.isShowMessage = true }) } this.hasVerified = true } } }
import InspectSpams from '@/components/InspectSpams' import inspectSpams from '@/mixins/inspectSpams' components: { InspectSpams },async save() { if(!this.hasVerified) { await this.inspectSpams(this.productName) } const variants = this.variants.map((variant) => { return { id: variant.id,} }) const params = { variants,name: this.productName } params.productId = this.productId await this.$store.dispatch('products/editProduct',params) .then(async() => { await this.getProductListData(this.productStatus,1) this.$message({ type: 'success',message: '產品修改成功!' }) }) .catch((message) => { this.$message({ type: 'error',message }) }) this.showEditProductDialog = false },<div class="edit-title">產品標題</div> <InspectSpams :isShowMessage="isShowMessage" :errorMessage="errorMessage"> <el-input v-model="productName" placeholder="請輸入產品標題" type="text" auto-complete="on" clearable @blur="inspectSpams(productName)" /> </InspectSpams>
優化與改進
1.優化呼叫敏感詞檢測的條件。如果產品標題沒有發生變化,則不需要再請求敏感詞檢測介面。因為只有經過檢驗成功的標題才能被成功儲存,所以無需多次重複呼叫介面,需要修改mixins請求介面的條件。
思路: 在根據產品id開啟模態框請求產品詳情介面的過程中,將對應產品資訊的name欄位賦值給新增的originalName變數。在html和js呼叫混入方法inspectSpams的過程中,將originalName和productName作為變數傳入即可。
//修改後的mixins混入 export default { data() { return { isShowM程式設計客棧essage: false,methods: { async inspectSpams(originalName,currentName) { this.hasVerified = false this.isShowMessage = false if (originalName !== currentName && currentName.trim() !== '') { await this.$store.dispatch('products/inspectSpams',currentName).程式設計客棧catch((err) => { this.errorMessage = err.response.data.message this.isShowMessage = true }) } this.hasVerified = true } } }
2.多次點選按鈕會重複請求介面。可以使用防抖、按鈕啟用倒計時、封裝axios請求、給button設定loading等方式進行優化。
寫在最後
其實總結得不是很好,編寫的元件也不夠好,思路也不太清晰,記錄在這裡主要是對思路二和思路四做個總結,畢竟對vue的文件還是不太熟悉。在敏感詞檢測元件中,其實還有很多可以改進的地方,歡迎大家在評論區中指出。
以上就是vue編寫檢測敏感詞彙元件的多種思路的詳細內容,更多關於vue編寫檢測敏感詞彙元件的資料請關注我們其它相關文章!