[TOC]

昨日回顾

网易云案例

  1. 开发前端服务需要接口- 用的nodejs服务-先启动该服务

    看一个nodejs服务或者前端服务启动命令,要看package.json中的scripts

    • start nodejs项目 或者react
      serve vue项目
      dev vue项目 react项目
  2. nodejs服务的地址和前端服务形成了跨域

    nodejs服务开放了cors

  3. 新建vue项目-初始化-选择了路由-安装了axios-vant-babel-plugin-import

  4. 需求分析-Layout(布局组件)- Home(首页)-Search(搜索页)- 播放页
  5. 路由关系

    layout- 布局<br />           -首页<br />           - 搜索页<br />       play - 同级
    
  6. 新建组件 layout- tabbar组件- navbar组件

  7. 封装axios ```javascript import axios from ‘axios’

const instance = axios.create({ baseURL: ‘’, timeout: 5000 }) // 请求拦截器 成功的回调函数。失败的回调函数 instance.interceptors.request.use(function(config) { // url headers cookie params. data return config // 会用这个config去请求 }, function (error) { return Promise.reject(error) // 抛出错误 })

// 响应拦截器 instance.interceptors.response.use(function(response) { // response就是响应的数据- 处理一些解构的问题 return response.data

}, function (error) { // 经常在这个位置判断 http的状态码 // 401- 表示token失效或者没传 // 如果是401- 删除token 重新登录

return Promise.reject(error) // 抛出错误 })

export default instance


8. 封装请求模块  api/home.js-好处是复用性
```javascript
import request from '@/utils/request

export function getResult(params) {
  return request({
    url,
    params
  })
}
  1. 首页获取

    先定义一个静态结构

  • 定义data数据接收
  • created获取数据- methods方法- created调用该方法
  • v-for循环生成静态结构
  1. 搜索页
  • 出现代码一定是代码出现了问题-只能看代码

    一定要敢改代码-下笔

  • 不管多么微小的想法,一定要落实到代码上

  • 点击热搜时-获取数据-watch
  • 热搜修改时-获取数据-watch
  • 上拉加载时-获取数据

1. 解决获取数据的重复问题

现在的问题是:数据一直在追加,势必造成id重复

  • 什么情况下要追加,什么情况下不能追加
  1. 上拉加载的时候追加- 加载下一页
  2. watch监听-关键字发生了变化-重新加载数据-赋值

    因为值改变和上拉加载公用了一个方法,给这个方法加了一个参数 isAppend

  async getSearchResult(keywords, isAppend = false) {
      const { data: { result: { songs } } } = await getSearchResult({ keywords, limit: 20, offset: (this.page - 1) * 20 })
      if(isAppend) {
        // 追加的情况放到后面
       this.searchList.push(...songs)
      }else {
        this.searchList = songs // 不追加就赋值
      }
    },

调用时- 上拉加载传true,值改变的时候不用传值

 async onLoad() {
      this.page++
      await this.getSearchResult(this.keyWord, true)
      // 此时认为执行完毕
      this.loading = false
 }

2. 当输入框的值为空的时候,有报错

keyWord 是空的,是查不出东西来的

  • 意味着当关键字清空的时候,之前的数据重置设置为空
  • 重置原来的数据 分页属性page 1 loading(false) finished(false), 不再发出请求

    watch: {
      keyWord(newValue) {
        // 当输入框的值为空的时候 重置数据
        if (!newValue) {
          this.searchList = []
          this.page = 1
          this.loading = false
          this.finished = false
          return
        }
        // 要去搜索
        this.getSearchResult(newValue)
      }
    },
    

    3. 当输入框的值发生变化-页码和数据问题

    当输入框的值发生变化,意味着重新搜索

  • 重新从第一页开始搜索

  • 搜一首歌曲 海底- 200条记录- 滑到底了之后,finished 应该为true, 再去搜一个歌曲 起风了
  • 重新设置finished loading也要重新设置

    watch: {
      keyWord(newValue) {
        // 当输入框的值为空的时候 重置数据
        this.page = 1
        this.loading = false
        this.finished = false
        if (!newValue) {
          this.searchList = []
          return
        }
        // 要去搜索
        this.getSearchResult(newValue)
      }
    },
    

    4. 当搜索不到内容时的报错问题

    当关键字搜索搜索不到数据的时候,由于我们直接用了解构赋值

  • 当搜索不到内容时-判断接口返回的数据 code为200 并且 songCount大于0的时候 才可以赋值

    否则- finished 设置为true

 async getSearchResult(keywords, isAppend = false) {
      const { data } = await getSearchResult({ keywords, limit: 20, offset: (this.page - 1) * 20 })
      const { code, result } = data // code为状态码200 表示加载成功- 获取result中的songCount > 0
      if (code === 200 && result.songCount > 0) {
        if (isAppend) {
          // 追加的情况放到后面
          this.searchList.push(...result.songs)
        } else {
          this.searchList = result.songs // 不追加就赋值
        }
      }else {
        // 此时意味着搜索不到数据 或者数据已经搜索完毕
        this.finished = true // 意味着获取数据已经结束
      }

    },

5. 搜索防抖的实现

watch监听了变量的更新

  • 防抖函数可以执行单位时间之内的最后一次
    watch: {
      keyWord(newValue) {
        // 当输入框的值为空的时候 重置数据
        this.page = 1
        this.loading = false
        this.finished = false
        if (!newValue) {
          this.searchList = []
          return
        }
       clearTimeout(this.timer) // 清空上一次的定时器
       this.timer = setTimeout(() => this.getSearchResult(newValue),300)
        // 要去搜索
      }
    },
    

6. 首页的布局问题

  • 先调整navbar组件的fixed属性,固定在顶部
  • 用div.main 包裹路由容器
    <template>
    <div>
      <van-nav-bar fixed :title="$route.meta.title"></van-nav-bar>
      <div class="main">
        <router-view></router-view>
      </div>
      <van-tabbar route>
        <van-tabbar-item replace to="/layout/home" icon="home-o">首页</van-tabbar-item>
        <van-tabbar-item replace to="/layout/search" icon="search">搜索</van-tabbar-item>
      </van-tabbar>
    </div>
    </template>
    

7. 封装统一的音乐的选项组件 SongItem

image.png

image.png

  • 可以封装一个统一的组件SongItem
  1. 公共的组件- components-SongItem ```vue


2. 替换原来的结构
```html
<van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
        <!-- <van-cell center v-for="item in searchList" :key="item.id" :title="item.name" :label="item.ar[0].name">
          <template #right-icon>
            <van-icon name="play-circle-o" size="0.6rem" />
          </template>
        </van-cell> -->
        <song-item v-for="item in searchList" :key="item.id" :item="item"></song-item>
      </van-list>

8.播放音乐的实现

  1. 准备了play组件 ```html


需要在SongItem中跳转到播放页面

```vue
<template>
    <van-cell center  :title="item.name" :label="item.name">
        <!-- 具名插槽 -->
        <template #right-icon>
            <van-icon name="play-circle-o" size="0.6rem" @click="toPlay" />
        </template>
    </van-cell>
</template>

<script>
export default {
  name: 'SongItem',
  props: {
    item: {
        type: Object,
        default: () => ({})  // eslint要求 default必须是一个函数
    }
  },
  methods: {
    toPlay() {
        this.$router.push({
            path: '/play',
            query: {
                id:  this.item.id
            }
        })

    }
  }
}
</script>

<style>
</style>