2021-09-03 黑马移动端头条项目-day06

目录

文章搜索模块

一、创建组件并配置路由

二、页面布局

三、处理页面显示状态

四、搜索联想建议

4.1 基本功能

4.2 防抖优化 ***

4.3 搜索关键字高亮

五、搜索结果

六、搜索历史记录

6.1 添加历史记录

6.2 展示历史记录

6.3 删除历史记录

6.4 数据持久化


文章搜索模块

一、创建组件并配置路由

1.创建src/views/search/index.vue

2.然后把搜索页面的路由配置到根组件路由(一级路由)

{
    path: '/search',
    name: 'search',
    component: () => import('@/views/search/')
}

3.home/index.vue搜索按钮添加页面导航,点击搜索跳转到search页

<van-button
        slot="title"
        icon="search"
        type="info"
        round
        size="small"
        class="search-btn"
        to="/search"
        >搜索</van-button
      >

二、页面布局

搜索栏 search/index.vue

<!-- Tips: 在van-search外层增加form标签,且action不为空,即可在ios输入法中显示搜索按钮 -->
    <form action="/">
      <van-search
        placeholder="请输入搜索关键词"
        v-model="searchText"
        @search="onSearch"
        @cancel="$router.back()"
        show-action
      />
    </form>

联想建议

1.创建独立组件search/components/search-suggestion.vue

2.search/index.vue中注册search-suggestion.vue组件

 3.布局

<template>
  <div class="search-suggestion">
    <van-cell title="hello" icon="search"> </van-cell>
  </div>
</template>

 搜索历史

创建独立组件search/components/search-history.vue

<template>
  <div>
    <van-cell title="搜索历史">
      <div>
        <span>全部删除</span>
        &nbsp; &nbsp;
        <span>完成</span>
      </div>
      <!-- <van-icon name="delete"></van-icon> -->
    </van-cell>
    <van-cell title="hello">
      <van-icon name="close"></van-icon>
    </van-cell>
  </div>
</template>

搜索结果

创建独立组件search/components/search-result.vue

<template>
  <div class="search-result">
    <van-list
      v-model="loading"
      :finished="finished"
      finished-text="没有更多了"
      @load="onLoad"
    >
      <van-cell v-for="item in list" :key="item" :title="item" />
    </van-list>
  </div>
</template>

三、处理页面显示状态

输入框有内容,显示联想建议;没有内容显示历史记录;前两个都不成立,则显示搜索结果

在data中初始化isResultShow: false  // 控制搜索结果的显示状态

<!-- 搜索结果 -->
    <search-result v-if="isResultShow" />
    <!-- 联想建议 -->
    <search-suggestion v-else-if="searchText" />
    <!-- 历史记录 -->
    <search-history v-else />

调整搜索结果样式:设置搜索结果的内部滚动条,不让搜索框随着下划滚动而滚动,只让搜索结果滚动,类似头部固定效果

.search-result {
  position: fixed;
  left: 0;
  right: 0;
  /* top: 54px; */
  top: 100px;
  bottom: 0;
  overflow-y: auto;
}

当输入框获得焦点时关闭搜索结果  @focus="isResultShow = false"

四、搜索联想建议

用户输入一个v,下方会显示有关v的搜索条目,效果如下显示:

4.1 基本功能

第一步-》监视数据变化:根据输入的内容展示联想建议的内容,每次输入需要发起请求

1.在父组件search/index.vue中将输入框里的内容searchText通过父子传值传给search-suggestion.vue

父组件: <search-suggestion v-else-if="searchText" :search-text="searchText" />

子组件:

  props: {
    searchText: {
      type: String,
      required: true
    }
  },

2.子组件监听数据变化 

watch: {
    // 要监视的数据名称
    searchText(){
      console.log('hello')
    }
  },

上方代码等价写法:

watch: {
    searchText: {
      handler() {
        console.log("hello");
      }
    }
  },

提示:此时会存在一个问题,组件未渲染出来,所以第一次的数据未监听到(只有数据渲染了才可以监听数据变化 )

优化: immediate: true 数据初始化时就监听,该回调将会在侦听开始之后立即调用

searchText: {
      handler() {
        console.log("hello");
      },
      immediate: true // 数据初始化时就监听
    }

第二步-》请求获取数据

配置网络请求接口 api/search.js

// 搜索相关请求模块
import request from '@/utils/request'

export const getSearchSuggestions = value => {
  return request({
    method: 'GET',
    url: '/app/v1_0/suggestion',
    params: {
      q: value
    }
  })
}

search-suggestion.vue请求获取数据、模板绑定展示

  watch: {
    searchText: {
      async handler() {
        // 找到数据接口
        // 请求获取数据
        const { data } = await getSearchSuggestions(this.searchText);
        console.log(data);
        // 模块绑定展示
        this.suggestions = data.data.options;
      },
      immediate: true // 数据初始化时就监听
    }
  },

用户输入一个v,就能出现含有v的数据即搜索建议,测试页面如下:

4.2 防抖优化 ***

用户输入一个字符就会发起一个请求,如输入abcd时a会发一次请求,b发一次,c发一次,d发一次,这里就会发4次请求,而实际上用户只想发一次请求 abcd。

 解决:

1.下载第三方包,包含函数节流和防抖功能: npm i lodash

2. 加载模块

// import _ from "lodash";  //导入所有函数
import { debounce } from "lodash"; // 按需导入 防抖函数

3.将handler处理函数进行防抖优化(到了1秒之后就开始调用 handler,不足1秒的话则请求发不出去)

    searchText: {
      // async handler() {
      //   // 找到数据接口
      //   // 请求获取数据
      //   const { data } = await getSearchSuggestions(this.searchText);
      //   // 模块绑定展示
      //   this.suggestions = data.data.options;
      // },
      // immediate: true // 数据初始化时就监听

      // 对上面代码进行 防抖函数 处理
      // 到了1秒之后就开始调用 handler,不足1秒的话则请求发不出去
      handler: debounce(async function() {
        const { data } = await getSearchSuggestions(this.searchText);
        this.suggestions = data.data.options;
      }, 1000),
      immediate: true
    }

4.输入abcd时只会发一次请求,测试结果如下:

 提示:创建一个debounce防抖函数,该函数会从上一次被调用后,延迟wait毫秒后调用func方法

4.3 搜索关键字高亮

将搜索的关键字能高亮显示,如输入vue,即在搜索建议里能把匹配的vue都设置高亮

 如何将字符串中的指定字符在网页中高亮展示?

1.显示字符的时候调用highlight()方法

    <!-- <van-cell
      :title="str"
      icon="search"
      v-for="(str, index) in suggestions"
      :key="index"
    >
    </van-cell> -->

    <!-- 将上方代码进行高亮优化 -->
    <!-- v-html可以识别html的标签内容 -->
    <van-cell icon="search" v-for="(str, index) in suggestions" :key="index">
      <div slot="title" v-html="highlight(str)"></div>
    </van-cell>

 2.编写highlight()方法

    highlight(str) {
      console.log(str); // 打印搜索建议里的每一项字符
      // 正则表达式:/中间的内容/ 都会当作正则匹配模式字符来对待
      // 所以在这里不能写 /this.searchText/gi

      // RegExp是正则表达式的构造函数,参数1:字符串;参数2:匹配模式;返回值:正则对象
      return str.replace(
        new RegExp(this.searchText, "gi"),
        `<span style="color: red">${this.searchText}</span>`
      );
    }

五、搜索结果

1.在父组件search/index.vue中将输入框里的内容searchText通过父子传值传给search-result.vue

父组件: <search-result v-if="isResultShow" :search-text="searchText" />

子组件:用props接收searchText值 

2.封装数据接口 

// 获取搜索结果
export const getSearchResult = params => {
  return request({
    method: 'GET',
    url: '/app/v1_0/search',
    params
  })
}

3.加载接口模块 import { getSearchResult } from "@/api/search";

4.处理onLoad事件函数

  • 先请求获取数据

  • 然后将数据放到数据列表中

  • 接着关闭本次的loading

  • 最近判断是否还有数据,有则更新获取下一页;没有则把finished设置为true,关闭加载更多

    async onLoad() {
      // 1.请求获取数据
      const { data } = await getSearchResult({
        q: this.searchText, //检索关键词
        page: this.page, //页数
        per_page: this.perPage //每页数量,per_page是接口里的字段值,要对应
      });
      // console.log(data);
      // 2.将数据放到数据列表中
      const { results } = data.data;
      this.list.push(...results);
      // 3.关闭本次的loading
      this.loading = false;
      // 4.判断是否还有数据,有则更新获取下一页;
      if (results.length) {
        page++;
      }
      // 没有则把finished设置为true,关闭加载更多
      else {
        this.finished = true;
      }
    }

5.页面遍历list数组,渲染在页面上

补充:

点击联想建议进入搜索

1.搜索栏绑定搜索事件@search="onSearch(searchText)",将点击的联想建议的标题填入输入框内进行搜索 this.searchText = searchText;

onSearch(searchText) {
     // 把输入框设置为你要搜索的文本
      this.searchText = searchText;
      // 展示搜索结果
      this.isResultShow = true;
    }

2.search-suggestion.vue自定义一个点击事件  @click="$emit('search', str)"

3.search/index.vue父组件接收该自定义事件  

     <search-suggestion
      v-else-if="searchText"
      :search-text="searchText"
      @search="onSearch"
    />

六、搜索历史记录

6.1 添加历史记录

6.2 展示历史记录

 记录并展示搜索历史记录

1.将存储搜索历史数据的数组searchHistories进行去重,若有重复的则splice去除

const index  = this.searchHistories.indexOf(searchText)
      if(index !== -1){
          this.searchHistories.splice(index,1)
}

2.将最新的历史记录放在最前面 this.searchHistories.unshift(searchText);

3.遍历历史记录的数组,由于searchHistories数组是写在父组件search/index.vue中的,需要通过父子传值方式把数组给search-history.vue子组件,

效果:

搜索历史记录的数据持久化

1.加载模块 import { setItem, getItem } from "@/utils/storage";

2.数据初始化 searchHistories: getItem("search-histories") || []

3.onSearch事件处理函数

    // 如果用户已登录,则把搜索历史记录存储到线上,这个操作无需手动存储
      // 提示:只要我们调用获取搜索结果的数据接口,后台会给我们自动存储用户的搜索历史记录
      // 如果没有登录,则把搜索历史记录存储到本地
      setItem("search-histories", this.searchHistories);

效果:

补充:ES6数组去重

合并数组: [...数组,...数组]   把Set转为数组:[...Set对象]

数组去重:[...new Set([...数组, ...数组])];

// 数组合并,去重
searchHistories = [...new Set([...searchHistories, ...data.data.keywords])];

6.3 删除历史记录

1.设置删除历史记录的处理状态

        isDeleteShow: false // 删除的显示状态

2.点击删除按钮时处理删除函数,这里还未进行数据持久化存储

   onDelete(history, index) {
      // 如果是删除状态
      if (this.isDeleteShow) { 
        return this.searchHistories.splice(index, 1);
         // 持久化
         // 1.修改本地存储的数据 
         // 2.请求接口删除线的数据
         
      }
      // 非删除状态,执行搜索状态,展示搜索结果
      this.$emit("search", history);
    }

3.父组件监听search事件,触发onSearch搜索结果的事件

<search-history
      v-else
      :search-histories="searchHistories"
      @search="onSearch"
    />

6.4 数据持久化

1.删除全部历史记录:

因为searchHistories数组search-history.vue组件是props里的数据,如果是引用类型(数组,对象)可以修改,注意这个修改指的是 user.name = 'jack‘,arr.push(123), arr.splice(0,1)

但是任何prop数据都不能重新赋值,xxx = xxx

如果想要让prop数据 = 新的数据,让父组件自己修改

<span @click="emit('update-histories', [])">全部删除</span>

父组件index.vue

<!-- 历史记录 -->
    <search-history
      v-else
      :search-histories="searchHistories"
      @search="onSearch"
      @update-histories="searchHistories = $event"
    />

2.删除全部历史记录数据持久化:

把index.vue和search-history.vue组件里的setItem("search-histories", this.searchHistories);全部注释,在index.vue中watch()里统一处理持久化操作

watch: {
    // 监视搜索历史记录的变化,存储到本地存储,删除全部历史记录的持久化
    searchHistories() {
      setItem("search-histories", this.searchHistories);
    }
  },

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值