关于音乐播放器项目详细解析

 

 

项目码云地址:https://gitee.com/Snow28/MyMusic(如果觉得可以,麻烦给我鼓励一个star)

这个是基于Vue2.0,vuex,vue-router,axios和html5的flexible box布局与及css3的transform,animation,transition组成。

技术栈

  • vue
  • vue-router
  • vuex
  • axios
  • jsonp
  • better-scroll
  • good-storage

主要功能:

  • 歌曲播放(从搜索历史添加到播放列表,展示最近播放)
  • 根据歌曲进度展示歌词,提供进度条
  • 歌曲收藏
  • 个人页面:显示个人的最近播放和个人收藏
  • 排行榜单页面: 几种榜单,详情页显示排行数字
  • 歌曲搜索(按人名,按歌名,删除搜索历史,无法搜索到歌曲页面): 搜索框监听内容变化显示搜索结果,搜索结果上拉加载,点击搜索结果添加到当前播放列表并播放该歌曲,搜索为歌手跳转歌手详情页;保存搜索历史,显示热门搜索标签
  • 热门歌单列表页面
  • 歌手页面:按姓氏首字母排列,点击侧面字母栏跳转到对应歌手区域
  • 推荐页面:推荐歌单
  • 播放页面: 旋转大头像,播放时间,进度条,切换歌曲(上一首,下一首) ,收藏,播放模式(单曲-顺序-随机),歌词页,播放按钮,迷你底部播放栏(播放页收起时显示)

文件目录:

项目是由vue-cli脚手架搭建的,

关于音乐播放器项目详细解析_第1张图片

关于src文件中源代码的内容:

关于音乐播放器项目详细解析_第2张图片

api处理:

项目数据全是线上抓取的,在qq音乐的官网可以抓取文件的url和相应的请求参数。

在抓取数据过程中,遇到了跨域问题,ajax并不能实现跨域,于用到了jsonp技术。在common文件夹下封装了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) : ''
}

处理了url的拼接和转码。

在base文件中,写的是在很多地方相同的功能组件,例如:搜索框组件,搜索列表主键,滚动功能组件,歌去列表展示组件,tab导航组件。

在comon文件中,存放了静态的字体文件,图片文件,stylus样式文件,还有一些封装好功能的js组件(如:数据缓存功能,还有不能jsonp请求的接口,需要用vue中proxy做代理的,还有在多个vue组件中都会用到的方法写在mixin.js文件中)

components文件就是我们开发主要功能目录

router文件中定义一个index.js文件,在里面管理路由的跳转

store文件中主要负责vuex代码,定义数据的state状态,修改并相应数据状态

main.js入口文件代码如下所示:

在入口文件引入了vue-router路由,store的数据存储状态,vuex全局状态管理,fastclick(避免移动端点击延迟300ms的效果)以及vue-lazyload懒加载模块。

关于音乐播放器项目详细解析_第3张图片

vue-router引入页面路由,对页面组件进行导航,其中其他引入大多是自定义的页面组件:

关于音乐播放器项目详细解析_第4张图片

store文件中:
关于vuex全局保存各个组件共享的数据与及改变数据的逻辑,全部保存在store的index.js里面,这是关键文件。包括利用数组模拟队列进行播放歌曲的保存,搜索历史的保存等关键逻辑。

关于音乐播放器项目详细解析_第5张图片

App.vue组件

是整个项目的根目录组件,在这里挂载其他所有的组件。m-header是QQ音乐的头部,tab组件是导航栏部分。router-view是将点击tab单项匹配到路由组件加载到这块内容。player就是音乐控制面板,当音乐详情关闭,则就以小化的面板控制音乐。

关于音乐播放器项目详细解析_第6张图片

vuex集中状态管理 :

  • 搜索历史,收藏列表,播放历史
  • 播放状态,播放模式,收起播放页,当前播放索引
  • 排行榜数据,推荐歌单数据,歌手数据(进入详情页使用)

完成该项目的难点:

数据抓取

存储本地数据和浏览器数据的业务逻辑,页面刷新后,数据状态不会改变

组件的复用

better-scroll 移动端插件(每次dom渲染要重新计算scroll宽度),封装成组件。

vuex状态管理的项目结构设计

几种历史记录收藏列表存储在localStorage

 

 

1.1 解决Vue动态路由参数变化,页面数据不更新

问题描述:

遇到动态路由如:/page/:id
从/page/1 切换到 /page/2 发现页面组件没有更新

解决方式:
增加一个不同:key值,这样vue就会识别这是不同的了。 


  ...
  computed:{
        key(){
            return this.$route.path + Math.random();
        }
    }

1.2 vue组件里定时器销毁问题

问题描述:
在a页面写一个定时器,每秒钟打印一次,然后跳转到b页面,此时可以看到,定时器依然在执行。
推荐的解决方式:
通过$once这个事件侦听器器在定义完定时器之后的位置来清除定时器。

const timer = setInterval(() => {
    // 定时器操作
}, 1000)

// 通过$once来监听定时器,在beforeDestroy钩子可以被清除。
this.$once('hook:beforeDestroy', () => {            
    clearInterval(timer);                                    
})

1.3 vue实现按需加载组件的两种方式

1.使用resolve => require(['./ComponentA'], resolve),方法如下:

const ComponentA = resolve => require(['./ComponentA'], resolve)
  1. 使用 () => import(), 具体代码如下:
const ComponentA = () => import('./ComponentA')

1.4 组件之间,父子组件之间的通信方案

组件之间的通信方案:

  • 通过事件总线(bus),即通过发布订阅的方式
  • vuex
  • 父子组件:
  • 父组件通过prop向自组件传递数据
  • 子组件绑定自定义事件,通过this.$emit(event,params) 来调用自定义事件
  • 使用vue提供的$parent/$children & $refs方法来通信
  • provide/inject
  • 深层次组件间的通信 $attrs, $listeners

实现细节可参考:https://m.jb51.net/article/167304.htm

1.5 vue中 $event 的用法--获取当前父元素,子元素,兄弟元素


  ...
  
  methods: {
   fun(e) {
    // e.target 是你当前点击的元素
    // e.currentTarget 是你绑定事件的元素
    #获得点击元素的前一个元素
    e.currentTarget.previousElementSibling.innerHTML
    #获得点击元素的第一个子元素
    e.currentTarget.firstElementChild
    # 获得点击元素的下一个元素
    e.currentTarget.nextElementSibling
    # 获得点击元素中id为string的元素
    e.currentTarget.getElementById("string")
    # 获得点击元素的string属性
    e.currentTarget.getAttributeNode('string')
    # 获得点击元素的父级元素
    e.currentTarget.parentElement
    # 获得点击元素的前一个元素的第一个子元素的HTML值
    e.currentTarget.previousElementSibling.firstElementChild.innerHTML
  
    }
        },

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Vue)