# QQMusic **Repository Path**: 12135/QQMusic ## Basic Information - **Project Name**: QQMusic - **Description**: a project based on vue.js - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-10-23 - **Last Updated**: 2021-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Music Player > 基于Vue的音乐播放器 > 说明:QQ音乐播放源时常更改,因此示例中的歌曲可能无法播放
### 手机扫一扫预览:  ### 一、播放器基本功能 - [x] 歌曲播放、切歌、进度控制 - [x] 三种播放模式的切换 - [x] 搜索歌手、歌曲 - [x] 上拉加载功能 - [x] 模拟登录跳转 - [x] 收藏歌曲 ### 二、概述 - 下面图片可以看出,有许多页面采用了复用的组件,比如推荐歌单页(图2)、歌手详情页(图4)、排行榜详情页(图8),同时在这些组件中还有更加细分的基础组件。这样可以高度定制化组件,满足不同的需求,提高开发效率。 - 核心是利用vuex做数据的传递,方便跟踪状态 - Vue-Router实现单页面路由跳转 - Vue-lazyLoad实现图片懒加载 - fastclick解决移动端300ms延迟 - 对搜索框搜索功能进行了节流,减少请求节约流量 - 使用vue提供的异步组件配合webpack的代码分割实现路由懒加载 - (2017.12.29新增)利用路由元信息的meta字段,通过watch $route动态改变transition的name,实现合理的动态路由切换过渡动画 - (2017.12.30新增)利用localStorage实现收藏歌曲功能,且该功能需在登录状态下操作(未登录时点击收藏按钮会自动跳转登录页) > * 这个播放器依然有许多隐藏的bug,还有许多功能待完善,抱着学习的态度我会一直更新完善它。 ### 三、图片预览 ##### 1.首页  ##### 2.推荐歌单页  ##### 3.歌手页  ##### 4.歌手详情页  ##### 5.播放器   ##### 6.排行榜  ##### 7.排行榜详情页  ##### 8.搜索页  ##### 9.搜索结果  ##### 10.个人中心  ### 四、项目结构 ```text │ App.vue //组件入口 │ main.js //js入口 │ ├─api //获取数据的文件 │ config.js //公共配置 │ deslist.js //热门歌单数据 │ lyric.js //歌词数据 │ rank.js //排行榜数据 │ rankDetail.js //榜单详情数据 │ recommend.js //轮播图数据 │ recommendDetail.js //热门歌单详情数据 │ result.js //搜索结果数据 │ search.js //热搜关键词数据 │ singerdetail.js //歌手详情数据 │ singerlist.js //歌手列表数据 │ ├─baseComponents //公用基础组件 │ ├─cannotfind │ │ cannotfind.vue //搜索结果为空 │ │ │ ├─circleProgress │ │ circleProgress.vue //环形进度条 │ │ │ ├─input │ │ input.vue //搜索框 │ │ │ ├─loading │ │ loading.svg │ │ loading.vue //加载中 │ │ │ ├─music │ │ music.vue //歌单列表 │ │ │ ├─progress │ │ progress.vue //进度条 │ │ │ ├─scroll │ │ scroll.vue //better-scroll的封装 │ │ │ ├─slider │ │ slider.vue //轮播图 │ │ │ └─songRank │ songrankcomplex.vue //榜单歌曲排序 │ songranksimple.vue //普通歌曲排序 │ ├─common //js工具库、样式、字体 │ ├─iconfont │ │ demo.css │ │ demo_fontclass.html │ │ demo_symbol.html │ │ demo_unicode.html │ │ iconfont.css │ │ iconfont.eot │ │ iconfont.js │ │ iconfont.svg │ │ iconfont.ttf │ │ iconfont.woff │ │ │ ├─js │ │ config.js //项目相关配置 │ │ dom.js //DOM操作方法 │ │ jsonp.js //jsonp的封装 │ │ mixins.js //vue提供的复用功能 │ │ prefixStyle.js //js中操作DOM添加前缀 │ │ singer.js //Singer类 │ │ song.js //Song类 │ │ localstorage.js //自制vue的localstorage插件 │ │ utils.js //函数工具库 │ │ │ └─stylus //stylus文件 │ base.styl │ index.styl │ mixin.styl │ myicon.styl │ reset.styl │ variable.styl │ ├─components //业务组件 │ ├─header │ │ header.vue //公用头部 │ │ logo@2y.png │ │ logo@3y.png │ │ │ ├─player │ │ player.vue //播放器组件 │ │ │ ├─rank │ │ rank.vue //排行榜组件 │ │ │ ├─rankDetail │ │ rankDetail.vue //排行榜详情组件 │ │ │ ├─recommend │ │ recommend.vue //首页 │ │ │ ├─recommendDetail │ │ recommendDetail.vue //首页详情组件 │ │ │ ├─result │ │ result.vue //搜索结果组件 │ │ │ ├─search │ │ search.vue //搜索页组件 │ │ │ ├─singer │ │ singer.vue //歌手列表组件 │ │ │ ├─singerDetail │ │ singerDetail.vue //歌手详情组件 │ │ │ ├─song │ │ song.vue //歌曲组件 │ │ │ ├─login │ │ login.vue //登录组件 │ │ │ ├─person │ │ person.vue //个人中心 │ └─tab │ tab.vue //头部导航组件 │ ├─router //路由配置 │ index.js │ └─vuex //vuex配置 actions.js //dispatch getters.js //计算state数据 index.js //vuex入口 mutations-types.js //mutations常量 mutations.js //commit state.js //基础数据 ``` ### 五、主要问题及解决方式 **起步**: Init the project by vue-cli(installed the dependencies by cnpm),and install babel-polyfill in dev-environment in order to compile es6'api(Array.from(),Object.assign()...) to javascript, also, installed babel-runtime and fastclick in pro-environment which the latter one is provided to solve "300ms delay" in mobile. **问题清单**: I got many troubles in doing this project,and now i would list them to remind me that i shoudn't be struggled in the same troubles again. ##### Q1: > * To obtain the data of QQ music, i need to use jsonp,but there is no way to get a more friendly API on GitHub, So i considered to create a more friendly API of JSONP: ```javascript import originJsonp from 'jsonp' //based on https://github.com/webmodules/jsonp export default function jsonp(url, data, option) { url += (url.indexOf('?') < 0 ? '?' : '&') + param(data) return new Promise((resolve, reject) => { originJsonp(url, option, (err, data) => { if (!err) { resolve(data) } else { reject(err) } }) }) } export function param(data) { let url = '' for (var k in data) { let value = data[k] !== undefined ? data[k] : '' url += '&' + k + '=' + encodeURIComponent(value) } return url ? url.substring(1) : '' } ``` ##### Q2: > * I use better-scroll to build my slider component,but the first question i found was that the slider component can't be rendered rightly.this problem was caused by :the slider component has been mounted before got the silder data with JSONP(async progress). so, in recommend component i need to add a judgment: ```html ``` ##### Q3: > * When import function that exported from recommend.js, it shows: `TypeError: Object(...) is not a function`,so i changed the way to export by not using `default`,and divide the functions into 2 js files,and troubles was dealed ##### Q4: > * When i got data of singers by JSONP,i found that the Data structure wasn't what i need. So i have to transform the structure by a series of ways into a two-dimensional Array. To be honest,it tooks me a lots of time,but any way,it deservered. ##### Q5: > * When i click the buttons of nextSong and prevSong to switch the currentSong,it shows error:`vue.runtime.esm.js?b7b3:6240 Uncaught (in promise) DOMException: The play() request was interrupted by a new load request`.I found that it's because the source of the song was not ready,so i designed an onoff to promise the songs can be played only when it's src is loaded,like this: ```html ``` ```js canplay(){ //标识:歌曲canplay时置为true this.canBePlayed = true }, error(){ //防止加载歌曲出错时播放器挂起 this.canBePlayed = true } nextSong(){ if( !this.canBePlayed ) { return } let index = this.currentIndex + 1 if( index === -1 ) { index = this.playList.length - 1 } this.setCurrentIndex(index) this.canBePlayed = false } ``` ##### Q7: > * 由于部分数据Host字段的限制,不能直接通过JSONP获取歌词数据,开发环境下采用后端代理来获取(生产环境需要另作修改) - 1.首先,对获取歌词数据的方法做出修改 ```js //获取数据的文件 import axios from 'axios' import {commonParams, options} from './config' export default function getLyric(mid){ //把正常请求的地址换为约定的后端代理地址 const url = '/api/getLyric' // 把公共参数和其他参数合并 const data = Object.assign( {}, commonParams, { pcachetime: + new Date() , songmid: mid, g_tk: 5381, loginUin: 0, hostUin: 0, format: 'json', platform:' yqq', needNewCode: 0 }) return axios.get( url, { params: data } ).then( (res) => { return Promise.resolve( res.data ) } ) } ``` - 2.在webpack-dev-conf.js中添加以下逻辑即可 ```js let express = require('express') let axios = require('axios') let app = express() let apiRoutes = express.Router() app.use('/api', apiRoutes) //找到devServer后在里面添加以下内容 before(app){ app.get( '/api/getLyric', function(req, res) { //这里是正常请求的地址 const url = 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg' //通过axios在nodejs中发送HTTP请求时,带上指定的headers以及params axios.get( url, { headers: { referer: 'https://y.qq.com/', host: 'c.y.qq.com' }, params: req.query } ).then((response) => { // res.json(response.data) //得到的歌词数据是JSONP形式,需要将其转为JSON var ret = response.data if (typeof ret === 'string') { var reg = /^\w+\(({[^()]+})\)$/ var matches = ret.match(reg) if (matches) { ret = JSON.parse(matches[1]) } } res.json(ret) } ).catch((err) => { console.log(err) }) } ) ``` ##### Q8: > * During developing the search component, i met a trouble that: it can't send request when i enter the character in input first time Whether the character is a letter or a singer word. And then i found it was caused by the props: ```html