Vue移动端系列 => [06] 文章搜索
六、文章搜索
6.1 创建组件并配置路由
1、给搜索按钮添加to属性
<!-- 导航栏 --><van-nav-bar class="page-nav-bar" fixed><van-buttonclass="search-btn"slot="title"type="info"size="small"roundicon="search"to="/search">搜索</van-button></van-nav-bar><!-- /导航栏 -->
1、创建 src/views/search/index.vue
<template><div class="search-container">搜索页面</div>
</template><script>export default {name: "SearchPage",components: {},props: {},data() {return {};},computed: {},watch: {},created() {},methods: {}};
</script><style scoped></style>
2、然后把搜索页面的路由配置到根组件路由(一级路由)
{path: '/search',component: Search
}
最后访问 /search
测试。
6.2 页面布局
6.2.1 搜索栏
1、搜索栏结构, vant组件搜索栏中的事件监听
<template><div class="search-container"><!-- 搜索栏》事件监听 --><form action="/"><van-searchv-model="value"show-actionplaceholder="请输入搜索关键词"@search="onSearch"@cancel="onCancel"/></form></div>
</template><script>
export default {name: 'search-container',data () {return {value: ''}},created () {},methods: {onSearch (val) {this.$toast(val)},onCancel () {this.$toast('取消')}}
}
</script><style scoped lang='less'></style>
6.2.2 完成 - 搜索历史
1、创建 src/views/search/components/search-history.vue
<template><div class="search-history"><van-cell title="搜索历史"><span>全部删除</span><span>完成</span><van-icon name="delete" /></van-cell><van-cell title="hello"><van-icon name="close" /></van-cell><van-cell title="hello"><van-icon name="close" /></van-cell><van-cell title="hello"><van-icon name="close" /></van-cell><van-cell title="hello"><van-icon name="close" /></van-cell></div>
</template><script>
export default {name: 'SearchHistory',components: {},props: {},data () {return {}},computed: {},watch: {},created () {},mounted () {},methods: {}
}
</script><style scoped lang="less"></style>
6.2.3 完成 - 联想建议
2、创建 src/views/search/components/search-suggestion.vue
<template><div class="search-suggestion"><van-cell title="程序员..." icon="search"></van-cell><van-cell title="程序员..." icon="search"></van-cell><van-cell title="程序员..." icon="search"></van-cell><van-cell title="程序员..." icon="search"></van-cell><van-cell title="程序员..." icon="search"></van-cell></div>
</template><script>
export default {name: 'SearchSuggestion',components: {},props: {},data () {return {}},computed: {},watch: {},created () {},mounted () {},methods: {}
}
</script><style scoped lang="less"></style>
6.2.4 完成 - 搜索结果
3、创建 src/views/search/components/search-result.vue
<template><div class="search-result"><van-listv-model="loading":finished="finished"finished-text="没有更多了"@load="onLoad"><van-cell v-for="item in list" :key="item" :title="item" /></van-list></div>
</template><script>
export default {name: 'SearchResult',components: {},props: {},data () {return {list: [],loading: false,finished: false}},computed: {},watch: {},created () {},mounted () {},methods: {onLoad () {// 异步更新数据// setTimeout 仅做示例,真实场景中一般为 ajax 请求setTimeout(() => {for (let i = 0; i < 10; i++) {this.list.push(this.list.length + 1)}// 加载状态结束this.loading = false// 数据全部加载完成if (this.list.length >= 40) {this.finished = true}}, 1000)}}
}
</script><style scoped lang="less"></style>
4、搜索组件内容如下:
<template><div class="search-container"><!-- 搜索栏 --><!--Tips: 在 van-search 外层增加 form 标签,且 action 不为空,即可在 iOS 输入法中显示搜索按钮--><form action="/"><van-searchv-model="searchText"show-actionplaceholder="请输入搜索关键词"background="#3296fa"@search="onSearch"@cancel="onCancel"/></form><!-- /搜索栏 --><!-- 搜索历史记录 --><search-history /><!-- /搜索历史记录 --><!-- 联想建议 --><search-suggestion /><!-- /联想建议 --><!-- 历史记录 --><search-result /><!-- /历史记录 --></div>
</template><script>
import SearchHistory from './components/search-history'
import SearchSuggestion from './components/search-suggestion'
import SearchResult from './components/search-result'export default {name: 'SearchIndex',components: {SearchHistory,SearchSuggestion,SearchResult},props: {},data () {return {searchText: ''}},computed: {},watch: {},created () {},mounted () {},methods: {onSearch (val) {console.log(val)},onCancel () {this.$router.back()}}
}
</script><style scoped lang="less">
.search-container {.van-search__action {color: #fff;}
}
</style>
6.3 处理页面展示逻辑
1、在 data
中添加数据用来控制搜索结果的显示状态
data () {...isResultShow: false
}
2、在模板中绑定条件渲染
<!-- 搜索结果 -->
<search-result v-if="isResult" />
<!-- /搜索结果 --><!-- 联想建议 -->
<search-suggestion v-else-if="searchText" />
<!-- /联想建议 --><!-- 搜索历史记录 -->
<search-history v-else />
<!-- /搜索历史记录 -->
3、触发搜索,显示搜索结果
onSearch (val) {this.isResult = true}
4、获取焦点,隐藏搜索结果
<van-searchv-model="searchText"show-actionplaceholder="请输入搜索关键词"@search="onSearch"@cancel="onCancel"@focus="isResult = false"/>
6.4 搜索联想建议
基本思路:
- 当搜索框输入内容的时候,请求加载联想建议的数据
- 将请求得到的结果绑定到模板中
6.4.1 获取并监听输入框内容的变化
一、将父组件中搜索框输入的内容传给联想建议子组件
<search-suggestion :searchText="searchText" v-else></search-suggestion>
二、在子组件中监视搜索框输入内容的变化,如果变化则请求获取联想建议数据
props: {searchText: {type: String,required: true}},watch: {searchText: {// 监视的处理函数handler (val) {console.log(val)},// 首次监视触发immediate: true}}
6.4.2 请求获取展示数据
一、将获取到的联想建议数据展示到列表中
定义获取建议的方法 search.js
import request from '@/utils/request'/*** 获取搜索联想建议*/ export const getSearchSuggestion = q => {return request({method: 'GET',url: '/app/v1_0/suggestion',params: {q}}) }
引入getSearchSuggestion
import { getSearchSuggestion } from '@/api/search.js'
定义加载数据的方法并调用getSearchSuggestion
watch: {searchText: {// 监视的处理函数handler (val) {this.loadSearchSuggestion(val)},// 首次监视触发immediate: true}},created () {},mounted () {},methods: {async loadSearchSuggestion (q) {try {const { data } = await getSearchSuggestion(q)this.suggestions = data.data.options} catch {this.$toast('获取失败')}}}
模板渲染
<div class="search-suggestion"><van-cellv-for="(item, index) in suggestions":key="index":title="item"icon="search"></van-cell></div>
6.4.3 防抖优化
1、安装 lodash
# yarn add lodash
npm i lodash
2、防抖处理
// lodash 支持按需加载,有利于打包结果优化
import { debounce } from "lodash"
不建议下面这样使用,因为这样会加载整个模块。
import _ from 'lodash' _.debounce()
// debounce 函数
// 参数1:函数
// 参数2:防抖时间
// 返回值:防抖之后的函数,和参数1功能是一样的
handler: debounce(function (val) {this.loadSearchSuggestion(val)
}, 1000)
6.4.5 搜索关键字高亮
6.4.5.1 思路分析
如何将字符串中的指定字符在网页中高亮展示?
"Hello World";
将需要高亮的字符包裹 HTML 标签,为其单独设置颜色。
"Hello <span style="color: red">World</span>"
在 Vue 中如何渲染带有 HTML 标签的字符串?
data () {return {htmlStr: 'Hello <span style="color: red">World</span>'}
}
<div>{{ htmlStr }}</div>
<div v-html="htmlStr"></div>
如何把字符串中指定字符统一替换为高亮(包裹了 HTML)的字符?
const str = "Hello World"// 结果:<span style="color: red">Hello</span> World
"Hello World".replace('Hello', '<span style="color: red">Hello</span>')// 需要注意的是,replace 方法的字符串匹配只能替换第1个满足的字符
// <span style="color: red">Hello</span> World Hello abc
"Hello World Hello abc".replace('Hello', '<span style="color: red">Hello</span>')// 如果想要全文替换,使用正则表达式
// g 全局
// i 忽略大小写
// <span style="color: red">Hello</span> World <span style="color: red">Hello</span> abc
"Hello World Hello abc".replace(/Hello/gi, '<span style="color: red">Hello</span>')
一个小扩展:使用字符串的 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>");
6.4.5.2 搜索关键字高亮
提前将文本设置为插槽
<van-cellv-for="(item, index) in suggestions":key="index"icon="search"><span slot="title" v-html="item"></span>
</van-cell>
1、在 methods 中添加一个方法处理高亮
/*** 处理高亮文本* 思路:* 1. 想要在一个字符串中,将固定的字符特殊显示(改变颜色)* 2. 那么就需要在这个字符串中,找出该字符,然后为该字符设置单独的样式(span.active)* 拆解:* 1. 找出字符* 2. 替换字符* 3. 设置单独的样式比较容易(替换字符),难点在于找出字符* 如何找出字符:* 1. 那么《处理高亮文本》的问题,就变成,《如何在字符串中找出固定的字符》* 2. 在字符串中找出固定字符,大家首先想到的就应该是使用 -》 正则表达式* 3. 简单使用正则(text.replace(/匹配的内容/gi, highlightStr)) , 无法插入响应式数据* 4. 所以我们使用了 RegExp 对象。RegExp 构造函数创建了一个正则表达式对象,用于将文本与一个模式匹配。MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp* 5. 通过 RegExp 来完成响应式数据的正则匹配*/highlightText(text) {const highlightStr = `<span class="active">${this.searchText}</span>`;// 正则表达式 // 中间的内容都会当作匹配字符来使用,而不是数据变量// 如果需要根据数据变量动态的创建正则表达式,则手动 new RegExp// RegExp 正则表达式构造函数// 参数1:匹配模式字符串,它会根据这个字符串创建正则对象// 参数2:匹配模式,要写到字符串中const reg = new RegExp(this.searchText, 'gi');// text.replace(/匹配的内容/gi, highlightStr)return text.replace(reg, highlightStr);}
2、然后在联想建议列表项中绑定调用
<!-- 联想建议 -->
<van-cell-group v-else-if="searchContent"><van-cellicon="search"v-for="(item, index) in suggestions":key="index"@click="onSearch(item)"><div slot="title" v-html="highlight(item)"></div></van-cell>
</van-cell-group>
<!-- /联想建议 -->
6.5 搜索结果
思路:
- 找到数据接口
- 请求获取数据
- 将数据展示到模板中
6.5.1 点击建议,传递搜索内容
点击联想进行搜索
<van-cellv-for="(item, index) in suggestions":key="index"@click="$emit('search', item)"icon="search"><span slot="title" v-html="highlight(item)"></span>
</van-cell>
定义自定义事件和函数
<!-- 搜索联想 -->
<search-suggestion @search="onSearch" v-else :search-text="value" />
// 事件函数
onSearch (val) {this.value = valthis.isShowSearchResult = true
}
6.5.2 处理完成
6.5.2.1 内容获取搜索关键字 - 处理完成
一、获取搜索关键字
1、声明接收父组件中的搜索框输入的内容
props: {searchText: {type: String,require: true}
}
2、在父组件给子组件传递数据
<!-- 搜索结果 -->
<search-result v-if="isResultShow" :searchText="searchText" />
<!-- /搜索结果 -->
最后在调试工具中查看确认是否接收到 props 数据。
二、请求获取数据
1、在 api/serach.js
添加封装获取搜索结果的请求方法
/*** 获取搜索结果*/
export function getSearch(params) {return request({method: "GET",url: "/app/v1_0/search",params})
}
2、请求获取
+ import { getSearch } from '@/api/search'export default {name: 'SearchResult',components: {},props: {q: {type: String,require: true}},data () {return {list: [],loading: false,finished: false,
+ page: 1,
+ perPage: 20}},computed: {},watch: {},created () {},mounted () {},methods: {+++ async onLoad () {// 1. 请求获取数据const { data } = await getSearch({page: this.page, // 页码per_page: this.perPage, // 每页大小q: this.q // 搜索关键字})// 2. 将数据添加到列表中const { results } = data.datathis.list.push(...results)// 3. 设置加载状态结束this.loading = false// 4. 判断数据是否加载完毕if (results.length) {this.page++ // 更新获取下一页数据的页码} else {this.finished = true // 没有数据了,将加载状态设置结束,不再 onLoad}}}
}
三、最后,模板绑定
<van-listv-model="loading":finished="finished"finished-text="没有更多了"@load="onLoad"
><van-cell
+ v-for="(article, index) in list"
+ :key="index"
+ :title="article.title"/>
</van-list>
6.5.2.2 错误提示
<van-listv-model="loading":finished="finished"finished-text="没有更多了":error.sync="error"error-text="请求失败,点击重新加载"@load="onLoad"><van-cell v-for="item in list" :key="item.art_id" :title="item.title" />
</van-list>
catch (err) {this.error = truethis.loading = false
}
6.5.2.3 顶部固定
.search {padding-top: 106px;.van-search {position: fixed;top: 0;left: 0;right: 0;z-index: 1;}.van-search__action {color: #fff;}
}
6.6 搜索历史记录
6.6.1 添加历史记录
当发生搜索的时候我们才需要记录历史记录。
1、在 data 中添加一个数据用来存储历史记录
data () {return {...searchHistories: []}
}
2、在触发搜索的时候,记录历史记录
onSearch (val) {// 更新文本框内容this.searchText = val// 存储搜索历史记录// 要求:不要有重复历史记录、最新的排在最前面const index = this.searchHistories.indexOf(val)if (index !== -1) {this.searchHistories.splice(index, 1)}this.searchHistories.unshift(val)// 渲染搜索结果this.isResultShow = true
},
6.6.2 展示历史记录
将数据传递给搜索历史组件
<!-- 搜索历史 -->
<search-history v-else-if="!value" :searchHistory="searchHistory" />
搜索历史组件接收数据
props: {searchHistory: {type: Array,required: true}
}
显示搜索历史
<!-- 历史记录 -->
<van-cell-group v-else><van-cell title="历史记录"><van-icon name="delete" /><span>全部删除</span> <span>完成</span></van-cell><van-cell:title="item"v-for="(item, index) in searchHistory":key="index"><van-icon name="close"></van-icon></van-cell>
</van-cell-group>
<!-- /历史记录 -->
6.6.3 删除历史记录
基本思路:
- 给历史记录中的每一项注册点击事件
- 在处理函数中判断
- 如果是删除状态,则执行删除操作
- 如果是非删除状态,则执行搜索操作
6.6.3.1 处理删除相关元素的展示状态
1、在 data 中添加一个数据用来控制删除相关元素的显示状态
data () {return {...isDeleteShow: false}
}
2、绑定使用
<!-- 历史记录 -->
<van-cell-group v-else><van-cell title="历史记录"><template v-if="isDeleteShow"><span @click="searchHistories = []">全部删除</span> <span @click="isDeleteShow = false">完成</span></template><van-icon v-else name="delete" @click="isDeleteShow = true"></van-icon></van-cell><van-cell:title="item"v-for="(item, index) in searchHistories":key="index"@click="onSearch(item)"><van-iconv-show="isDeleteShow"name="close"@click="searchHistories.splice(index, 1)"></van-icon></van-cell>
</van-cell-group>
<!-- /历史记录 -->
6.6.3.2 处理删除操作
<!-- 历史记录 -->
<van-cell-group v-else><van-cell title="历史记录"><template v-if="isDeleteShow">
+ <span @click="searchHistories = []">全部删除</span> <span @click="isDeleteShow = false">完成</span></template><van-icon v-else name="delete" @click="isDeleteShow = true" /></van-cell><van-cell:title="item"v-for="(item, index) in searchHistories":key="index"
+ @click="onHistoryClick(item, index)"><van-icon v-show="isDeleteShow" name="close"></van-icon></van-cell>
</van-cell-group>
<!-- /历史记录 -->
onHistoryClick (item, index) {// 如果是删除状态,则执行删除操作if (this.isDeleteShow) {this.searchHistory.splice(index, 1)} else {// 否则执行搜索操作}
}
删除全部
<van-cell title="搜索历史"><div v-if="isDeleteShow"><span @click="$emit('clear-search-history')" style="margin-right: 10px;">全部删除</span><span @click="isDeleteShow = false">完成</span></div><van-icon v-else @click="isDeleteShow = true" name="delete" />
</van-cell>
<van-cell:title="item"v-for="(item, index) in searchHistory":key="index"@click="onHistoryClick(item, index)"><van-icon v-if="isDeleteShow" name="close"></van-icon>
</van-cell>
<search-history @clear-search-history="searchHistories = []" :searchHistory="searchHistories" v-else-if="!searchText"></search-history>
点击搜索
onHistoryClick (item, index) {// 如果是删除状态,则执行删除操作if (this.isDeleteShow) {this.searchHistory.splice(index, 1)} else {// 否则执行搜索操作this.$emit('search', item)}
}
<search-history @search="onSearch" @clear-search-history="searchHistories = []" :searchHistory="searchHistories" v-else-if="!searchText"></search-history>
6.6.4 数据持久化
1、利用 watch 监视统一存储数据
watch: {searchHistories (val) {// 同步到本地存储setItem('serach-histories', val)}
},
2、初始化的时候从本地存储获取数据
data () {return {...searchHistories: getItem('serach-histories') || [],}
}
Vue移动端系列 => [06] 文章搜索相关推荐
- Vue移动端系列 => [07] 文章详情
七.文章详情 创建组件并配置路由 1.创建 views/article/index.vue 组件 <template><div class="article-contain ...
- 前端系列——vue2+高德地图web端开发(poi搜索两种方式)
前端系列--vue2+高德地图web端开发(poi搜索) 前言 基础 什么是poi搜索 1. 输入提示结合poi搜索 官方代码 步骤 1.进行plugins插件注册 2.data中编写placeSea ...
- 16.app端文章搜索
一.app端文章搜索 1.搭建ElasticSearch环境 1.1拉取镜像 docker pull elasticsearch:7.4.0 1.2创建容器 docker run -id --name ...
- Vue移动端搜索页面的历史记录写法
Vue移动端搜索页面的历史记录写法 前言 本文用于记录对于搜索页的历史记录的学习. 预览 先上两张图,分别为展示隐藏搜索列表和历史记录. html部分 <template><hips ...
- vue 移动端跳页搜索自动获取焦点
需求:点击搜索框跳转新页面然后自动获取焦点弹起键盘. 遇到的问题: input的autofocus属性在android上可以,在ios上不兼容,ios为了安全起见,禁掉了. 解决方案: 使用子路由做新 ...
- Vue移动端项目(一)
一.项目初始化 目标 能使用 Vue CLI 创建项目 了解 Vant 组件库的导入方式 掌握制作使用字体图标的方式 掌握如何在 Vue 项目中处理 REM 适配 理解 axios 请求模块的封装 模 ...
- Docker系列06—基于容器制作镜像并上传到Docker Registry
Docker系列06-基于容器制作镜像并上传到Docker Registry 1.制作镜像 1.1 镜像的生成途径 基于容器制作 dockerfile,docker build 本篇主要详细讲解基于容 ...
- 手摸手,带你用vue撸后台 系列一(基础篇) - 掘金
完整项目地址:vue-element-admin 系列文章: 手摸手,带你用 vue 撸后台 系列一(基础篇) 手摸手,带你用 vue 撸后台 系列二(登录权限篇) 手摸手,带你用 vue 撸后台 系 ...
- 源码解读_入口开始解读Vue源码系列(二)——new Vue 的故事
作者:muwoo 转发链接:https://github.com/muwoo/blogs/blob/master/src/Vue/2.md 目录 入口开始解读Vue源码系列(一)--造物创世 入口开始 ...
最新文章
- 解决sqlserver1433端口占用问题
- element input 只能输入数字_Python之input()函数
- [Win32]一个调试器的实现(四)读取寄存器和内存
- arm 指令1(转)
- SAP 电商云 Spartacus UI 出现 breaking change 时,如何用文字来描述
- 51Nod-1002 数塔取数问题【DP】
- 1219v网卡驱动+linux,黑苹果Intel板载网卡驱动-IntelMausiEthernet.kext下载 V2.5.0d0-PC6苹果网...
- ResultSet获取记录条数
- 计蒜客:Adjoin the Networks
- 菜哥学知识图谱(通过“基于医疗知识图谱的问答系统”)(三)(代码分析)
- 解析 Github 的默认头像
- MSK的CPM调制和I-Q调制
- git 修改远端 commit 信息
- 运用Java获取当前时间
- 黑马程序员——【学习笔记】集合——String、StringBuffer 以及集合
- 光电二极管放大电路的应用
- JDBC连接oracle11g——socket read timed out
- css实现弧线,CSS3 彩虹弧线太极波动图
- Python webdriver.Chrome()的使用
- centos mysql ssh连接,使用SSH隧道连接MYSQL
热门文章
- 2017.10.11 米加特官网重磅改版,华丽上线
- JWT、JWS与JWE
- linux中的execl函数使用
- 计算机考试音乐怎么粘贴,用手机或电脑怎样剪切和接音乐?
- 2021年保育员(中级)考试及保育员(中级)考试资料
- 6-12 二叉搜索树的操作集 (30 分)
- 极度未知HyperX20周年盛惠—HyperX Cloud 2 飓风FPS耳机听音辨位
- SQL实战(9)--从titles表获取按照title进行分组
- powershell 报错 0xffff0000 的解决方法
- html中怎么写css代码,html style样式标签元素教程