zoukankan      html  css  js  c++  java
  • 【音乐App】—— Vue-music 项目学习笔记:歌单及排行榜开发

    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记。

    项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star。


    歌单及详情页 排行榜及详情页
    一、歌单详情页开发

           歌单详情页布局介绍及Vuex实现路由数据通讯

    • components->disc目录下:创建disc.vue
    • router->index.js中:给Recommend添加二级子路由Disc
      {
          path: '/recommend',
          component: Recommend,
          children: [
              {
                  path: ':id',
                  component: Disc
              }
          ]
      }
    • recommend.vue中:
    1. 添加路由容器
      <router-view></router-view>
    2. 给歌单列表添加点击事件
      @click="selectItem(item)"
    3. 定义selectItem()实现路由跳转
      selectItem(item) {
          this.$router.push({
              path: `/recommend/${item.dissid}`
         })
      }
    • 使用vuex传递歌单数据
    1. state.js中:添加歌单对象
      disc: {}
    2. mutation-type.js中:定义type常量
      export const SET_DISC = 'SET_DISC'
    3. mutation.js中:创建更改函数
      [types.SET_DISC](state, disc){
          state.disc = disc
      }
    4. getters.js中:定义数据映射
      export const disc = state => state.disc
    • recommend.vue中:使用mapMutations修改disc
      import {mapMutations} from 'vuex'
      
      ...mapMutations({
          setDisc: 'SET_DISC'
      })
      
      selectItem(item) {
          this.$router.push({
               path: `/recommend/${item.dissid}`
          })
         this.setDisc(item) //更改state中的disc
      }
    • disc.vue中:通过mapGetters获取到date中的disc
      import {mapGetters} from 'vuex
      
      computed: {
         ...mapGetters([
               'disc'
         ])
      }
    • 传递数据:获取数据在computed里面,设置数据在methods里面

           歌单详情页数据抓取

    • api->recommend.js中:添加getSonglist接口
      export function getSongList (disstid) {
           const url = '/api/getSongList'
      
           const data = Object.assign({}, commonParams, {
               uin: 0,
               format: 'json',
               notice: 0,
               needNewCode: 1,
               new_format: 1,
               pic: 500,
               disstid, //关键数据
               type: 1,
               json: 1,
               utf8: 1,
               onlysong: 0,
               picmid: 1,
               nosign: 1,
               song_begin: 0,
               platform: 'h5',
               song_num: 100,
               _: +new Date()
          })
      
          return axios.get(url, {
               params: data
          }).then((res) => {
               return Promise.resolve(res.data)
          })
      }
    • 因为数据也经过加密,在webpack.dev.config.js中模仿header:
      app.get('/api/getSongList', function (req, res) {
           var url = 'https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg'
           axios.get(url, {
                 headers: {
                      referer: 'https://y.qq.com/',
                      host: 'c.y.qq.com'
                 },
                 params: req.query
          }).then((response) => {
                 res.json(response.data)
          }).catch((e) => {
                 console.log(e)
          })
      })

      注意:后端配置之后,一定要重新启动!!!

           歌单详情页数据的处理和应用

    • 调用getSongList()方法获取数据,在created()时获取
      import {getSongList} from '@/api/recommend'
      
      _getSongList () {
           if(!this.disc.dissid){ //在歌单详情页强制刷新后,即没有获得id时,回退到推荐页面
               this.$router.push('/recommend')
               return
           }
           getSongList(this.disc.dissid).then((res) => {
               if (res.code === ERR_OK) {
                   this.songs = this._normalizeSongs((res.cdlist[0].songlist))
                    // console.log(res)
                    // console.log(res.cdlist[0].songlist)
                    // console.log(this._normalizeSongs(res.cdlist[0].songlist))
               }
          })
      }
    • 获得数据后,在methods中对数据进行一些处理
    1. 同歌手详情页,歌曲的播放url中的vkey需要发送请求获取,同时将处理好的数据封装新的Song实例
      import {ERR_OK} from '@/api/config'
      import {creatSongList} from '@/common/js/song'
      import {getMusic} from '@/api/singer'
      
      _normalizeSongs (list) {
          let ret = []
          list.forEach((musicData) => {
               if (musicData.id && musicData.album) {
                      // ret.push(creatSongList(musicData))
                      getMusic(musicData.mid).then((res) => {
                             // console.log(res)
                             if(res.code === ERR_OK){
                                  // console.log(res.data)
                                  const svkey = res.data.items
                                  const songVkey = svkey[0].vkey
                                  const newSong = creatSongList(musicData, songVkey)
                                   ret.push(newSong)
                             }
                      })
               }
          })
          return ret
      }

      其中:调用getMusic()获取播放地址的vkey,调用creatSongList()实例化Song对象

    2. common->js->song.js中: 创建creatSongList()
      export function creatSongList (musicData, songVkey) {
                return new Song({
                       id: musicData.id,
                       mid: musicData.mid,
                       singer: filterSinger(musicData.singer),
                       name: musicData.name,
                       album: musicData.albumname,
                       duration: musicData.interval,
                       image: `https://y.gtimg.cn/music/photo_new/T002R300x300M000${musicData.album.mid}.jpg?max_age=2592000`,
                       //注意guid以实时数据为主
                       url: `http://ws.stream.qqmusic.qq.com/C400${musicData.mid}.m4a?vkey=${songVkey}&guid=6319873028&uin=0&fromtag=66`
               })
      }
    二、排行榜及详情页开发

           排行榜数据抓取

    • 在api目录下:创建rank.js添加获取数据的接口
      import jsonp from '@/common/js/jsonp'
      import {commonParams, options} from './config'
      
      export function getTopList() {
           const url = "https://c.y.qq.com/v8/fcg-bin/fcg_myqq_toplist.fcg"
      
           const data = Object.assign({}, commonParams, {
                    platform: 'h5',
                    needNewcode: 1
           })
      
           return jsonp(url, data, options)
      }
    • rank.vue中:调用getTopList()方法获取数据,在created()时获取
      import {getTopList} from '@/api/rank'
      import {ERR_OK} from '@/api/config'
      
      created() {
          this._getTopList()
      },
      methods: {
          _getTopList() {
                getTopList().then((res) => {
                      if(res.code === ERR_OK){
                             console.log(res.data.topList)
                      }
                })
          }
      }

           排行榜数据应用

    • 在data中维护数据
      topList: []
    • 获取数据后赋值
      this.topList = res.data.topList
    • 将数据填入DOM:应用scroll组件替换父元素<div>并传入topList数据实现滚动
      <scroll class="top-list" :data="topList">
    • 优化:应用loading组件,设置当topList没有内容时显示loading
    • 优化:应用mixin,实现播放器底部适配

           榜单详情页布局及Vuex实现路由数据通讯

    • 路由的跳转
    1. router->index.js中:设置Rank的二级路由
      import TopList from '@/components/top-list/top-list' 
      
      {
          path: '/rank',
          component: Rank,
          children: [
              {
                    path: ':id',
                    component: TopList
               }
          ]
      }
    2. rank.vue中:添加路由容器,给列表添加点击事件,触发路由的跳转

      <router-view></router-view>
      @click="selectItem(item)"
      selectItem(item) {
          this.$router.push({
               path: `/rank/${item.id}`
          })
      }
    • Vuex传递数据
    1. state.js中:定义数据
      topList: {}
    2. mutation-types.js中:定义type常量
      export const SET_TOP_LIST = 'SET_TOP_LIST'
    3. mutations.js中:添加修改topList的方法
      [types.SET_TOP_LIST](state, topList){
           state.topList = topList
      }
    4. getters.js中:添加topList的数据映射
      export const topList = state => state.topList
    • 写入和获取state数据
    1. rank.vue中:通过mapMutations写入数据
      import {mapMutations} from 'vuex'
      
      this.setTopList(item)
      
      ...mapMutations({
          setTopList: 'SET_TOP_LIST'
      })
    2. top-list.vue中:通过mapGatters拿到数据
      import {mapGetters} from 'vuex'
      
      computed: {
          ...mapGetters([
              'topList'
         ])
      }

      剩下的就是获取数据用computed,设置数据用methods

           榜单详情歌曲数据的抓取

    • 需求:榜单本身的背景图不好看,想要抓取榜单详情歌曲的图片作为背景图
    • 问题:原请求是Ajax请求,不能跨域,但也支持jsonp请求,只是没有格式化不方便查看数据
    • 解决: 在线网站www.bejson.com 格式化为json文件
    • 因为这块数据QQ音乐也做了加密,这里抓取数据同歌单详情页方式
    1. api->rank.js中:添加获取数据的接口
      export function getMusicList(topid) {
           const url = "https://c.y.qq.com/v8/fcg-bin/fcg_v8_toplist_cp.fcg"
      
           const data = Object.assign({}, commonParams, {
                    page: 'detail',
                    type: 'top',
                    tpl: 3,
                    platform: 'h5',
                    needNewcode: 1
           })
      
           return jsonp(url, data, options)
      }
    2. top-list.vue中:调用getMusicList()方法获取数据,在created()时获取
      import {getMusicList} from '@/api/rank'
      import {ERR_OK} from '@/api/config'
      
      created() {
          this._getMusicList()
      }
      
      getMusicList(this.topList.id).then((res) => {
          if(res.code === ERR_OK){
             this.songs = this._normalizeSongs(res.songlist)
             // console.log(res.songlist)
          }
      })
    3. 获得数据后,在methods中对数据进行一些处理:同歌单详情页,歌曲的播放url中的vkey需要发送请求获取,同时将处理好的数据封装新的Song实例
      import {getMusic} from '@/api/singer'
      import {createSong} from '@/common/js/song'
      
      _normalizeSongs(list) {
            let ret = []
            list.forEach((item) => {
                  const musicData = item.data
                  // console.log(musicData)
                  if (musicData.songid && musicData.albumid) {
                      getMusic(musicData.songmid).then((res) => {
                             // console.log(res)
                             if(res.code === ERR_OK){
                                 // console.log(res.data)
                                 const svkey = res.data.items
                                 const songVkey = svkey[0].vkey
                                 const newSong = createSong(musicData, songVkey)
                                 ret.push(newSong)
                             }
                     })
                 }
           })
           return ret
      }

      这样数据就获取到了,剩下的把songs的数据传递给子组件就可以了

    • 把bgImage的图片换成榜单第一首歌的图片
      bgImage() {
         if (this.songs.length) {
             return this.songs[0].image
         }
         return this.topList.picUrl
      }

           带排行的song-list组件的扩展和应用

    • 添加rank的DOM结构、图片以及样式
    • 添加props字段rank,默认false,设置只有当rank为true时排行显示
      rank: {
          type: Boolean,
          default: false
      }
      <div class="rank" v-show="rank">
    • 定义两个方法分别获得动态绑定的class和文案
      <span :class="getRankCls(index)">{{getRankText(index)}}</span>
      getRankCls(index) {
           if(index<=2) {
               return `icon icon${index}`
           }else{
               return 'text'
          }
      },
      getRankText(index) {
          if(index > 2) {
               return index + 1
          }
      }
    • music-list.vue作为中间组件,也需要扩展一个props字段rank
      rank: {
           type: Boolean,
           default: false
      }
    1. 传入<song-list>
      <song-list :rank="rank"></song-list>
    2. 这样就把最外层父组件传入的rank应用到了song-list.vue组件中
    3. top-list.vue中维护数据rank为true,同时传入<music-list>中

     注:项目来自慕课网

  • 相关阅读:
    cas 重复登录问题解决了。但是两个页签都是已登录状态,一个页签退出,另外一个页签正常访问应用程序。会出现下面报错信息。待解决
    flask 结合cas 出现的问题
    实验场景:两个页面登录状态下,一个logout.然后重新登录,一个页面刷新
    flask 生成excel并下载
    为什么不能在shell脚本中执行source /etc/profile或者source ~/.bashrc问题?
    centos7实现ssh免秘钥分发
    Drbd+Heatbeat实现NFS共享文件存储高可用
    docker监控方案实践(cadvisor+influxdb+grafana)
    centos7.5部署ELk
    keepalived+haproxy实现高可用负载均衡
  • 原文地址:https://www.cnblogs.com/ljq66/p/10168769.html
Copyright © 2011-2022 走看看