目录
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 忽略大小写
-
② 然后在联想建议列表项中绑定调用
③ 拓展方法:使用字符串的 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 监视统一存储数据
② 初始化的时候从本地存储获取数据