开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情
前言
本篇是搜索页实现的中篇,我们将使用防抖函数、网络请求取消实现模糊搜索与搜索结果的关键字标红展示功能。
知识点
函数防抖的使用 网络请求取消 搜索结果关键字标红
一、防抖函数
需求场景说明:我们希望用户在输入搜索关键字时,能立即触发接口查询,减少用户操作。但是输入框的 input 事件会在用户输入时持续触发,所以我们需要限制该事件的频率,比如用户在输入完上一个字后 100ms 内未再次输入,我们就认为用户输入结束,发起网络请求。防抖函数的作用,就是实现延时请求与频率限制。
1. 防抖函数的实现
防抖函数的实现有很多的教程,再次不做过多赘述。
/**
* @desc 防抖函数
* @param {function} func 目标函数
* @param {number} [wait=100] 延迟执行毫秒数
*/
export const debounce = (func, wait = 100) => {
let timeout;
return function (event) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.call(this, event);
}, wait);
};
}
2. 防抖函数的挂载
我们将防抖函数定义在 utils/index.js 文件中,并在 app.js 中将其作为参数传递给 App。
import { debounce } from "./utils/index"; // 防抖
App({
debounce
})
3. 防抖函数的使用
因为我们将搜索框与搜索结果模块拆分成了组件,所以我们需要通过他们的父组件来实现消息传递。
现在有个问题:防抖函数的处理是放在搜索组件还是搜索查询的组件中?
我个人觉得放在搜索查询页面(search-result 组件)更好,因为我们最终想要控制的是接口查询的频率,放在这里控制职责更清晰些。
所以我们在 search-result 组件中发起网络请求的方法前做了如下处理:
methods: {
// 查询方法,注意此处的写法
doSearch: app.debounce(function () {
this.search();
}),
// 搜索关键字查询
async search() {
// 接口调用 略
}
}
二、取消请求
1. 为什么需要取消请求
原因:虽然有防抖函数降低了请求频率,但是它无法避免会有多次请求发起。举个例子,用户想输入“钢铁侠”三个字搜索影视,当用户输入“钢铁”后停顿了下,此时接口发起请求,我们记为 request_1 ,在请求过程中用户继续输入“侠”字,接口又发起一个请求,记为 request_2,当 request_1 如果耗时更久还未返回时,request_2却先返回了,之后 request_1 才返回,这时搜索结果将会展示 request_1 的内容,造成查询关键字与显示结果不匹配。
解决方案:接口有新请求时取消上次的请求。
取消请求是客户端层面的事件,后端接口还是会接收到接口请求并返回,只是客户端不接收。
2. 取消请求的实现
我们利用小程序 wx.request 的 abort() 方法来中断请求,主要代码如下:
Component({
data: {
keyword: "", // 查询关键字
page: 1, // 当前页码
movies: [], // 查询结果列表
_cancel: null, // 网络请求任务对象
},
methods: {
// 搜索接口
async search() {
// 如果当前已存在请求任务,则取消该任务
if (this.data._cancel) {
this.data._cancel.abort();
this.data._cancel = null;
}
const params = {
page: this.data.page,
keyword: this.data.keyword
}
// 将请求任务对象赋值给 _cancel 字段
this.data._cancel = wx.request({
url: HOST + '/search',
data: params,
header: {
'content-type': 'application/json',
Authorization: `Bearer ${app.token}`
},
success: ({ code, data, total }) => {
if (code === 200) {
this.setData({
movies: this.data.movies.concat(data),
page: ++this.data.page,
})
}
},
fail: (err) => {
console.error(err);
},
complete: () => {
// 请求完成后将请求人物对象置空
this.data._cancel = null;
}
})
}
}
})
我们之前接口在发起请求时,一般会设置个
loading状态标志来阻止接口请求过程中被再次触发,在这里是不能做这个限制的,多次的请求会被中断掉。
三、关键字标红
1. 效果展示
如下图所示,我们搜索关键字“钢铁侠”时,返回的影视列表的影视名称中会把关键字“钢铁侠”标红。
2. 标红实现
1) web 中实现
如果是在 web 中,我们只需要使用字符串替换函数,将关键字使用标签进行包裹,然后给标签设置样式即可。vue 代码实现如下:
computed: {
title() {
reurn this.movie.title.replace(this.keyword, `<span class="keyword">${this.keyword}</span>`);
}
}
<div v-html="title"></div>
之后我们给 .keyword 增加样式即可。
2) 小程序中实现(使用 rich-text 标签)
微信从基础库 1.4.0 开始支持了 rich-text 标签,它可以解析受信任的 html 标签,所以在逻辑部分我们可以按照 web 的方式处理,模板部分代码如下:
<rich-text nodes="{{ title }}" />
3) 小程序中实现(拆分数组遍历)
之前在没有 rich-text 标签时,该怎么实现呢?
我们可以将标题按照关键字进行拆分为数组,如“钢铁侠3”拆分为 ['钢铁侠', '3'],之后再使用 wx:for 遍历该数组,如果数组元素与关键字一致,则给其赋予不同的样式,代码如下:
computed: {
title(data) {
return data.movie.title.replace(new RegExp(`${data.keyword}`, 'g'), `%%${data.keyword}%%`).split('%%')
}
}
模板中遍历该数组,如果元素与关键字一致,则赋予 .keyword 样式:
<block wx:for="{{ title }}">
<text class="{{ keyword === item ? 'keyword' : '' }}">{{ item }}</text>
</block>
总结
以上即为搜索页实现的中篇,主要讲述了防抖函数的使用,取消请求的实现,以及搜索结果关键字标红的处理。
下一篇,我们将完成搜索页的剩余部分:tab 页切换与历史搜索记录的实现。