06.vue.2.X开发音乐App-歌手页面

github:https://github.com/Ching-Lee/vue-music

1.分析后台数据

从QQ音乐网页版获取后台数据




这里的回掉函数是callback

  • 创建歌手页面的请求文件


import jsonp from '../assets/js/jsonp'
import {commonParams, optionsPc} from './config'

export default function getSingerList () {
  const url = 'https://u.y.qq.com/cgi-bin/musicu.fcg'
  const data = {
    'comm': {
      'ct': 24,
      'cv': 10000
    },
    'singerList': {
      'module': 'Music.SingerListServer',
      'method': 'get_singer_list',
      'param': {
        'area': -100,
        'sex': -100,
        'genre': -100,
        'index': -100,
        'sin': 0,
        'cur_page': 1
      }
    }
  }
  // 实现将多个对象拷贝到同一个对象中
  const param = Object.assign({}, commonParams,
    {
      loginUin: 0,
      hostUin: 0,
      format: 'jsonp',
      platform: 'yqq',
      needNewCode: 0,
      data: JSON.stringify(data)
    })
  // 返回值就是promise
  return jsonp(url, param, optionsPc)
}

  • 在config.js中一些参数做了改变
// 配置通用参数
export const commonParams = {
  g_tk: 5381,
  inCharset: 'utf-8',
  outCharset: 'utf-8',
  notice: 0
}

// 配置jsonp库的通用的options
export const options = {
  // 通过qq得到了回掉函数的参数名
  param: 'jsonpCallback'
}

// PC端的回掉函数
export const optionsPc = {
  // 通过qq得到了回掉函数的参数名
  param: 'callback'
}
  • singer.vue组件中获取数据
<script type="text/ecmascript-6">
import getSingerList from '../../api/singer'
export default {
  data () {
    return {
      singerlist: []
    }
  },
  created () {
    this._getSingerList()
  },
  methods: {
    _getSingerList () {
      getSingerList().then((result) => {
        this.singerlist = result.singerList.data.singerlist
      }, (err) => { console.log(err) }
      )
    }
  }
}

2.我们将得到的数据根据country聚类

要实现的效果
  • singer.vue中添加方法
  _singerCountryMap () {
    // 将数据按照地点区分
      let map = {}
      for (let value of this.singerlist) {
        let key = value.country
        if (!map[key]) {
          let item = []
          map[key] = item
        }
        map[key].push(new Singer(value))
      }
      return map
    }
  }
  • Singer类里面存储了和歌手相关的信息,图片的地址是根据singer_mid得到的


export default class Singer {
  constructor (value) {
    this.country = value.country
    this.singer_id = value.singer_id
    this.name = value.singer_name
    this.singer_pic = 'http://y.gtimg.cn/music/photo_new/T001R150x150M000' + value.singer_mid + '.jpg?max_age=2592000'
  }
}

3.创建listView组件


遍历data对象,对于每一个城市的键值对是一个li
然后在li中又嵌套遍历该城市的value值(是该城市的歌手的数组)。

<template>
<ul>
  <li v-for="(value, key, index) in data" v-bind:key="index">
    <h2 class="title">{{key}}</h2>
    <ul>
      <li v-for="(item, index) in (value)" v-bind:key="index" class="singer_item">
        <img v-bind:src="item.singer_pic" class="singerPic">
        <span class="singer_name">{{item.name}}</span>
      </li>
    </ul>
  </li>
</ul>
</template>
<script type="text/ecmascript-6">
export default {
  props: {
    data: Object,
    default: null
  }
}
</script>

<style>
  .title{
    height: 2rem;
    background-color: darkorange;
    color: whitesmoke;
    padding: 0.25rem 1rem;
    line-height: 2rem;
    margin-bottom: 0.5rem;
  }
  .singerPic{
    width: 5rem;
    border-radius: 50%;
  }
  .singer_item{
    padding: 0.5rem 1rem;
    position:relative;
  }
  .singer_name{
   color: white;
    margin-left: 2rem;
    position: absolute;
    bottom: 50%;
    transform: translate(0,50%);
  }
</style>

  • 在singer.vue中调用该组件
<template>
  <div class="singer">
    <listview v-if="singerlist.length" v-bind:data=" _singerCountryMap ()"></listview>
  </div>
</template>
<style>
 .singer{
   background-color: orange;
 }
</style>

3.图片懒加载

我们现在是一次性加载所有的图片,会影响性能,这里应该使用图片懒加载
安装vue-lazyload插件



在main.js中引入vue.lazyload

import VueLazyLoad from 'vue-lazyload'
Vue.use(VueLazyLoad, {
  loading:require('./assets/images/music_logo.png')
})

就会进行首屏加载,之后滚动到要显示的地方会再加载。
在listveiw中更改,使用v-lazy标签

 <ul>
      <li v-for="(item, index) in (value)" v-bind:key="index" class="singer_item">
        <img v-lazy="item.singer_pic" class="singerPic">
        <span class="singer_name">{{item.name}}</span>
      </li>
    </ul>

4.正在载入loading组件


<template>
  <div class="loading">
    <img src="./loading.gif">
    <p class="dec">{{title}}</p>
  </div>

</template>

<script type="text/ecmascript-6">
export default {
  props: {
    title: {
      type: String,
      default: '正在载入...'
    }
  }
}
</script>

<style>
.loading{
  position: absolute;
  top:50%;
  left:50%;
  transform: translate(-50%,-50%);
  text-align: center;
}
.loading p{
  font-size: 14px;
}

</style>

在歌手组件中调用loading组件,使用v-show,在列表没有长度的时候显示,有长度不显示

<template>
  <div>
    <div class="singer" v-if="singerlist.length">
      <listview  v-bind:data=" _singerCountryMap ()"></listview>
    </div>
    <div v-show="!singerlist.length">
      <loading></loading>
    </div>
  </div>
</template>

5.快速导航入口

  • 首先添加计算属性,获取到所有城市的名称:
computed: {
    shortcutList () {
      let keylist = []
      for (let key in this.data) {
        if (key) {
          keylist.push(key)
        }
      }
      return keylist
    }
  },
  • 在template中添加快速入口的div
 <div v-if="data">
      <ul ref="quickNav" class="shortpart" @click="onShortcutTouchStart">
        <li v-for="(key,index) in shortcutList" v-bind:key="index" class="shortitem" v-bind:data-index="index">
          {{key}}
        </li>
      </ul>
    </div>
mounted () {
    this.$nextTick(function () {
      this.citylist = this.$refs.roll.children
      this.headerHeight = this.citylist[0].offsetTop
      this.quicknavlist = this.$refs.quickNav.children
      this.scrollListener()
    })
  },

整个ul使用了固定定位。

<style>
  .title{
    height: 2rem;
    background-color: darkorange;
    color: whitesmoke;
    padding: 0.25rem 1rem;
    line-height: 2rem;
    margin-bottom: 0.5rem;
  }
  .singerPic{
    width: 5rem;
    border-radius: 50%;
  }
  .singer_item{
    padding: 0.5rem 1rem;
    position:relative;
  }
  .singer_name{
   color: white;
    margin-left: 2rem;
    position: absolute;
    bottom: 50%;
    transform: translate(0,50%);
  }
  .shortpart{
    position: fixed;
    top:50%;
    right:0;
    transform: translate(0,-25%);
    width:4rem;
    text-align: center;
  }
  .shortitem{
    margin: 1rem 0;
    color: black;
    font-size: 12px;
  }
</style>
  • 可以看到给ul注册了点击事件,使用了事件委托的原理。
  • scrollTo的意思就是把传入参数的x,y坐标移动到浏览器(0,0)点。
  • offsetLeft 和 offsetTop 返回的是相对于 offsetParent 元素的距离,而 offsetParent 指的是一个元素最近的父级定位元素,如果没有定位元素就是文档根节点。所以会超出视窗

点击了之后通过event.target拿到被点击元素的li,获取到data-index属性,然后去遍历左边的城市大的li,如果这个li的索引和data-index相同,就去计算出当前这个li距离可视窗口顶部的距离,然后减去头部和导航栏的距离,就是让这个计算出的高度滚动到(0,0)点。

methods: {
    onShortcutTouchStart (event) {
      // 点击的快速入口的li
      let current = event.target
      // 拿到点击的索引
      let index = current.getAttribute('data-index')
      // 遍历各个城市的li(每个li里面嵌套了title和ul(里面是该城市的歌手))
      for (let liIndex in this.citylist) {
        // 如果点击的这个快速入口的索引和
        if (liIndex === index) {
          let height = this.citylist[liIndex].offsetTop - this.headerHeight
          window.scrollTo(0, height)
        }
      }
    },
  • 之后我们添加一个滚动监听事件,看各个城市的标题出现在屏幕中,我们就让快速导航栏颜色变白,同时他的前一个或者后一个如果是白的,就让他变黑并break。
    这里用到了事件节流
 scrollListener () {
      let _self = this
      let timeout
      window.addEventListener('scroll', function () {
        if (timeout) {
          clearTimeout(timeout)
        }
        // 事件节流
        timeout = setTimeout(callback(_self), 100)
      })
      function callback (_self) {
        for (let index = 0; index < _self.citylist.length; index++) {
          // 把标题那一行给拿出来,h2
          let title = _self.citylist[index].getElementsByTagName('h2')[0]
          let titleTop = title.offsetTop - (document.body.scrollTop || document.documentElement.scrollTop)
          let currentli = _self.quicknavlist[index]
          if (currentli) {
            if (titleTop >= _self.headerHeight && titleTop <= document.documentElement.clientHeight) {
              currentli.style.color = 'white'
              if (_self.quicknavlist[index - 1].style.color === 'white') {
                _self.quicknavlist[index - 1].style.color = 'black'
              }
              if (_self.quicknavlist[index + 1].style.color === 'white') {
                _self.quicknavlist[index + 1].style.color = 'black'
              }
              break
            }
          }
        }
      }
    },
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容