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: '',

errorMessage: '',

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({

type: 'success',

message: '产品修改成功!'

})

})

.catch((message) => {

this.$message({

type: 'error',

message

})

})

this.showEditProductDialog = false

}

}

思路三:使用mixins抽取敏感词检测方法

  本以为敏感词检测就这样离我而去了,结果收到产品消息,同样需要给定制模块的产品标题增添敏感词检测。之后就是一顿ctrl+c和ctrl+v操作猛如虎,最后发现代码冗余过多,需要对代码进行整理。此处和后续都只写产品模块的敏感词检测逻辑,定制模块的敏感词检测逻辑其实和产品模块大同小异。 既然定制模块的产品标题编辑和产品模块的样式和逻辑相差无几,那何不抽取敏感词检测公共方法呢?就这么愉快地决定了,键盘上一顿狂敲乱打后,混入方法就成型了:

export default {

data() {

return {

hasVerified: false,

showMessage: 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,

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({

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,

errorMessage: '',

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,

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({

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,

errorMessage: '',

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

},

mixins: [ inspectSpams ],

async save() {

if(!this.hasVerified) {

await this.inspectSpams(this.productName)

}

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({

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 {

isShowMessage: false,

errorMessage: '',

hasVerified: true

}

},

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编写检测敏感词汇组件的资料请关注其它相关文章!

以上是 vue实现检测敏感词过滤组件的多种思路 的全部内容, 来源链接: utcz.com/p/239087.html

回到顶部