六、文章搜索

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 请求获取展示数据

一、将获取到的联想建议数据展示到列表中

  1. 定义获取建议的方法 search.js

    import request from '@/utils/request'/*** 获取搜索联想建议*/
    export const getSearchSuggestion = q => {return request({method: 'GET',url: '/app/v1_0/suggestion',params: {q}})
    }
    
  2. 引入getSearchSuggestion

    import { getSearchSuggestion } from '@/api/search.js'
    
  3. 定义加载数据的方法并调用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('获取失败')}}}
    
  4. 模板渲染

    <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>&nbsp;&nbsp;<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>&nbsp;&nbsp;<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>&nbsp;&nbsp;<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] 文章搜索相关推荐

  1. Vue移动端系列 => [07] 文章详情

    七.文章详情 创建组件并配置路由 1.创建 views/article/index.vue 组件 <template><div class="article-contain ...

  2. 前端系列——vue2+高德地图web端开发(poi搜索两种方式)

    前端系列--vue2+高德地图web端开发(poi搜索) 前言 基础 什么是poi搜索 1. 输入提示结合poi搜索 官方代码 步骤 1.进行plugins插件注册 2.data中编写placeSea ...

  3. 16.app端文章搜索

    一.app端文章搜索 1.搭建ElasticSearch环境 1.1拉取镜像 docker pull elasticsearch:7.4.0 1.2创建容器 docker run -id --name ...

  4. Vue移动端搜索页面的历史记录写法

    Vue移动端搜索页面的历史记录写法 前言 本文用于记录对于搜索页的历史记录的学习. 预览 先上两张图,分别为展示隐藏搜索列表和历史记录. html部分 <template><hips ...

  5. vue 移动端跳页搜索自动获取焦点

    需求:点击搜索框跳转新页面然后自动获取焦点弹起键盘. 遇到的问题: input的autofocus属性在android上可以,在ios上不兼容,ios为了安全起见,禁掉了. 解决方案: 使用子路由做新 ...

  6. Vue移动端项目(一)

    一.项目初始化 目标 能使用 Vue CLI 创建项目 了解 Vant 组件库的导入方式 掌握制作使用字体图标的方式 掌握如何在 Vue 项目中处理 REM 适配 理解 axios 请求模块的封装 模 ...

  7. Docker系列06—基于容器制作镜像并上传到Docker Registry

    Docker系列06-基于容器制作镜像并上传到Docker Registry 1.制作镜像 1.1 镜像的生成途径 基于容器制作 dockerfile,docker build 本篇主要详细讲解基于容 ...

  8. 手摸手,带你用vue撸后台 系列一(基础篇) - 掘金

    完整项目地址:vue-element-admin 系列文章: 手摸手,带你用 vue 撸后台 系列一(基础篇) 手摸手,带你用 vue 撸后台 系列二(登录权限篇) 手摸手,带你用 vue 撸后台 系 ...

  9. 源码解读_入口开始解读Vue源码系列(二)——new Vue 的故事

    作者:muwoo 转发链接:https://github.com/muwoo/blogs/blob/master/src/Vue/2.md 目录 入口开始解读Vue源码系列(一)--造物创世 入口开始 ...

最新文章

  1. 解决sqlserver1433端口占用问题
  2. element input 只能输入数字_Python之input()函数
  3. [Win32]一个调试器的实现(四)读取寄存器和内存
  4. arm 指令1(转)
  5. SAP 电商云 Spartacus UI 出现 breaking change 时,如何用文字来描述
  6. 51Nod-1002 数塔取数问题【DP】
  7. 1219v网卡驱动+linux,黑苹果Intel板载网卡驱动-IntelMausiEthernet.kext下载 V2.5.0d0-PC6苹果网...
  8. ResultSet获取记录条数
  9. 计蒜客:Adjoin the Networks
  10. 菜哥学知识图谱(通过“基于医疗知识图谱的问答系统”)(三)(代码分析)
  11. 解析 Github 的默认头像
  12. MSK的CPM调制和I-Q调制
  13. git 修改远端 commit 信息
  14. 运用Java获取当前时间
  15. 黑马程序员——【学习笔记】集合——String、StringBuffer 以及集合
  16. 光电二极管放大电路的应用
  17. JDBC连接oracle11g——socket read timed out
  18. css实现弧线,CSS3 彩虹弧线太极波动图
  19. Python webdriver.Chrome()的使用
  20. centos mysql ssh连接,使用SSH隧道连接MYSQL

热门文章

  1. 2017.10.11 米加特官网重磅改版,华丽上线
  2. JWT、JWS与JWE
  3. linux中的execl函数使用
  4. 计算机考试音乐怎么粘贴,用手机或电脑怎样剪切和接音乐?
  5. 2021年保育员(中级)考试及保育员(中级)考试资料
  6. 6-12 二叉搜索树的操作集 (30 分)
  7. 极度未知HyperX20周年盛惠—HyperX Cloud 2 飓风FPS耳机听音辨位
  8. SQL实战(9)--从titles表获取按照title进行分组
  9. powershell 报错 0xffff0000 的解决方法
  10. html中怎么写css代码,html style样式标签元素教程