目录
1. 创建组件并配置路由
2. 页面布局
3. 处理页面显示状态
4. 搜索联想建议
4.1 基本功能
4.2 防抖优化
4.3 搜索关键字高亮
5. 搜索结果
5.1 获取搜索关键字
5.2 请求获取数据
5.3 模板绑定
6. 搜索历史记录
6.1 添加历史记录
6.2 展示历史记录
6.3 删除历史记录
6.4 数据持久化
1. 创建组件并配置路由
① 创建 src/views/search/index.vue
② 然后把搜索页面的路由配置到根组件路由(一级路由)
2. 页面布局
① 创建 src/views/search/components/searchHistory.vue
② 创建 src/views/search/components/searchSuggestion.vue
③ 创建 src/views/search/components/searchResult.vue
④ 搜索组件内容如下:
-
搜索栏
-
van-search 组件
-
在 van-search 外层增加 form 标签,且 action 不为空,即可在 iOS 输入法中显示搜索按钮。
-
show-action 是否在搜索框右侧显示取消按钮
-
-
搜索结果、搜索联想建议、搜索历史记录
3. 处理页面显示状态
需求:
- 用户点击搜索后显示搜索结果列表,否则显示搜索历史或者是联想建议
- 当输入框获得焦点的时候,显示搜索列表或者是联想建议
- 当输入框内有搜索文本的时候显示联想建议,没有则显示搜索历史
① 在 data
中添加数据用来控制搜索结果的显示状态
② 在模板中绑定条件渲染
4. 搜索联想建议
- 当搜索框输入内容的时候,请求加载联想建议的数据
- 将请求得到的结果绑定到模板中
4.1 基本功能
① 将父组件中搜索框输入的内容传给联想建议子组件
② 在子组件中监视搜索框输入内容的变化,如果变化则请求获取联想建议数据
- 获取联系建议接口
-
不加立即侦听的话,searchValue初次改变的时候,搜索联想建议组件还未渲染完成,所以拿不到值
③ 将获取到的联想建议数据展示到列表中
-
在 Vue 2.x 中,当 v-if 和 v-for 同时存在于同一个元素上时,由于 v-for 优先级更高,会导致性能问题,因为 v-for 先执行,遍历整个列表,然后每个项目再根据 v-if 条件进行判断渲染。
-
在 Vue 3.x 中,当 v-if 和 v-for 同时存在于同一个元素上时,v-if 优先级更高。
4.2 防抖优化
- 节流与防抖?
- 节流:就是指连续触发事件但是在 n 秒中只执行一次函数。(王者荣耀技能冷却,期间内无法继续释放技能)
- 防抖:单位时间内,频繁触发的事件,只执行最后一次。(英雄联盟英雄回城,如果被打断了,只能重新B回城)
- 更多性能优化理解参考:ES6学习笔记(八)——性能优化(节流与防抖)
使用 lodash 库实现防抖优化:
① 安装 lodash
- yarn add lodash 或者 npm i lodash
② 防抖处理
- lodash 支持按需加载,有利于打包结果优化
- 不建议下面这样使用,因为这样会加载整个模块。
- import _ from 'lodash'
- _.debounce()
-
debounce(参数1, 参数2)
-
参数1: 一个函数,不建议使用箭头函数,因为箭头函数的 this 指向上层作用域。
-
参数2: 延迟时间,时间是毫秒
-
返回值:防抖之后的函数值
-
4.3 搜索关键字高亮
- 如何将字符串中的指定字符在网页中高亮展示?
- 将需要高亮的字符包裹 HTML 标签,为其单独设置颜色。
- "Hello <span style="color: red">World</span>"
- 在 Vue 中如何渲染带有 HTML 标签的字符串?
- 使用 v-html 指令
data () {
return {
htmlStr: 'Hello <span style="color: red">World</span>'
}
}
//模版
<div>{{ htmlStr }}</div> //无法渲染为标签
<div v-html="htmlStr"></div>
- 如何把字符串中指定字符统一替换为高亮(包裹了 HTML)的字符?
let str = 'hello world 哈哈哈 hello world World'
// replace('需要替换的字符', '替换后的字符')
// 默认只替换匹配到的第一个字符
let newStr = str.replace('world', 'vue')
console.log(newStr) //hello vue 哈哈哈 hello world World
// g全局匹配,不止替换第一个字符
// i不区分大小写
let reg = /world/gi
let newStr2 = str.replace(reg, 'vue')
console.log(newStr2) // hello vue 哈哈哈 hello vue vue
// findIndex 是 JavaScript 数组方法,
// 用于找到数组中第一个符合测试函数条件的元素的索引。如果没有找到符合条件的元素,则返回 -1。
const array = [5, 12, 8, 130, 44]
const index = array.findIndex(item => item === 8)
console.log(index) //2
下面是具体的步骤处理:
① 在 methods 中添加一个方法处理高亮
-
如果后端返回的联系建议的数组中为空的前况下,sourceText 的值为空 null,此时 sourceText.replace 就会报错,在空的前况下,我们就让返回原有的搜索值
-
/this.searchContent/ 的写法是错误的,正则表达式中的一切内容都会当做字符串使用
-
RegExp 是原生 JavaScript 的内置构造函数
-
参数1:字符串,注意,这里不要加 //
-
参数2:匹配模式,g 全局,i 忽略大小写
-
-
正则表达式复习参考:JavaScript学习笔记(六)——BOM(Window对象)——十、正则表达式
② 然后在联想建议列表项中绑定调用
③ 拓展方法:使用字符串的 split 结合数组的 join 方法实现高亮
var str = "hello world 你好 hello";
// ["", " world 你好 ", ""]
const arr = str.split("hello");
// "<span>hello</span> world 你好 <span>hello</span>"
arr.join("<span>hello</span>");
5. 搜索结果
思路:
- 找到数据接口
- 请求获取数据
- 将数据展示到模板中
5.1 获取搜索关键字
① 声明接收父组件中的搜索框输入的内容
② 在父组件给子组件传递数据
5.2 请求获取数据
① 在 api/serach.js
添加封装获取搜索结果的请求方法
② 请求获取
- List 列表 onLoad 加载数据公式:
1. 请求获取数据
2. 将结果添加到数组列表中
3. 将本次加载中的 loading 关闭
4. 判断是否还有数据
如果有,则更新获取下一页数据
如果没有,则将加载状态设置 finished 设置为结束
5. 加载失败,加载 loading 状态也要停用
5.3 模板绑定
6. 搜索历史记录
需求:
- 非删除状态
- 点击列表项,以列表项中的关键字搜索结果
- 删除状态,点击删除图标显示“全部删除”和“完成”和叉叉小图标。
- 删除状态点击列表删除历史记录
6.1 添加历史记录
当发生搜索的时候我们才需要记录历史记录。
① 在父组件 data 中添加一个数据用来存储历史记录
② 在触发搜索的时候,记录历史记录
-
用户点击搜索,存储搜索历史记录
-
不要有重复历史记录,最新的历史记录排在最前面
6.2 展示历史记录
6.3 删除历史记录
基本思路:
- 给历史记录中的每一项注册点击事件
- 在处理函数中判断
- 如果是删除状态,则执行删除操作
- 如果是非删除状态,则执行搜索操作
① 处理删除相关元素的展示状态
- 在 data 中添加一个数据用来控制删除相关元素的显示状态
- 绑定使用
② 处理删除操作
- 全部删除
- 删除某条历史记录或者以某条历史为关键字搜索(即触发父组件中的 onSearch 方法)
6.4 数据持久化
由于后端接口仅支持存储四条历史记录,所以本次持久化采用本地缓存的的方式实现
① 利用 watch 监视统一存储数据
② 初始化的时候从本地存储获取数据