资讯专栏INFORMATION COLUMN

用VUEJS做一个网易云音乐

894974231 / 1975人阅读

摘要:我自己一直喜欢用网易云音乐,于是乎就做了这个。后台后台采用做为后台提供系统请求所用的源码,原理很简单就是用伪装成一个客户端去访问网易云音乐的然后,把返回的数据转发出来。

</>复制代码

  1. 前言:自己学习VUEJS也一段时间,但一直没有做出来一东西。我自己一直喜欢用网易云音乐app,于是乎就做了这个app。

技术栈

vue全家桶 (vue vue-router vuex)

axios

Muse-UI(一个基于Vue2.x的material design 风格UI框架)

功能与思路分析

我之前学习JS的时候对Html5 audio研究过,也写过一些例子,那时的功能并不是很全面。在写这个程序之前,我好好的查阅了当前的HTML5中的audio标签,发现园子上一位园友总结的很不错(这里)。于是就先把网易云音乐最基本的功能实现,歌单部分(这也是我喜欢网易云音乐的原因这一),然后实现音乐的上一曲、下一曲,播放、暂停。列表功能。

后台

后台采用.net做为后台提供系统请求所用的API(源码),原理很简单就是用.net伪装成一个客户端去访问网易云音乐的API然后,把返回的json数据转发出来。同时服务端做下跨域处理。

核心代码:

</>复制代码

  1. ///
  2. /// 请求网易云音乐接口
  3. ///
  4. /// 要请求的接口类型
  5. /// 要请求的接口类型的对象
  6. /// 请求结果(JSON)
  7. public static string Request(T config) where T : RequestData, new()
  8. {
  9. // 请求URL
  10. string requestURL = config.Url;
  11. // 将数据包对象转换成QueryString形式的字符串
  12. string @params = config.FormData.ParseQueryString();
  13. bool isPost = config.Method.Equals("post", StringComparison.CurrentCultureIgnoreCase);
  14. if (!isPost)
  15. {
  16. // get方式 拼接请求url
  17. string sep = requestURL.Contains("?") ? "&" : "?";
  18. requestURL += sep + @params;
  19. }
  20. HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestURL);
  21. req.Accept = "*/*";
  22. req.Headers.Add("Accept-Language", "zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4");
  23. // 如果服务端启用了GZIP,那么下面必须解压,否则一直乱码。
  24. // 参见:http://www.crifan.com/set_accept_encoding_header_to_gzip_deflate_return_messy_code/
  25. req.Headers.Add("Accept-Encoding", "gzip,deflate,sdch");
  26. req.ContentType = "application/x-www-form-urlencoded";
  27. req.KeepAlive = true;
  28. req.Host = "music.163.com";
  29. req.Referer = "http://music.163.com/search/";
  30. req.UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537";
  31. // 设置cookies
  32. req.Headers["Cookie"] = "appver=1.5.2";
  33. req.Method = config.Method;
  34. req.AutomaticDecompression = DecompressionMethods.GZip;
  35. if (isPost)
  36. {
  37. // 写入post请求包
  38. byte[] formData = Encoding.UTF8.GetBytes(@params);
  39. // 设置HTTP请求头 参考:https://github.com/darknessomi/musicbox/blob/master/NEMbox/api.py
  40. req.GetRequestStream().Write(formData, 0, formData.Length);
  41. }
  42. // 发送http请求 并读取响应内容返回
  43. return new StreamReader(req.GetResponse().GetResponseStream(), Encoding.GetEncoding("UTF-8")).ReadToEnd();
  44. }
vuejs部分

项目结构

</>复制代码

  1. ├── index.html
  2. ├── main.js
  3. ├── api
  4. │ └── ... # 抽取出API请求
  5. ├── components
  6. │ ├── playBar.vue
  7. │ └── ...
  8. └── store
  9. │ └── index.js # 整个项目的vuex部分
  10. └── router
  11. │ └── router.js # 整个项目的路由
  12. └── utils # 一些工具类模块
  13. └── views # 项目中的一些route-view

</>复制代码

  1. 说项目的路由之前,先来看一张效果图

对于整个项目来说:视图区别在于顶部导航,下面的bar的是否出来取决于,当前系统列表中是否有歌曲,如果有就会出现。

router.js核心部分

</>复制代码

  1. const router = new VueRouter({
  2. mode: "history",
  3. routes: [{
  4. path: "/index",
  5. component: require("../views/index"),
  6. children: [
  7. {
  8. path: "rage",
  9. component: require("../views/rage")
  10. },
  11. {
  12. path: "songList",
  13. component: require("../views/songList")
  14. },
  15. {
  16. path: "leaderBoard",
  17. component: require("../views/leaderBoard")
  18. },
  19. {
  20. path: "hotSinger",
  21. component: require("../views/hotSinger")
  22. }
  23. ]
  24. }, {
  25. name: "playerDetail",
  26. path: "/playerDetail/:id",
  27. component: require("../views/playerDetail")
  28. }, {
  29. path: "/playListDetail/:id",
  30. name: "playListDetail",
  31. component: require("../views/playListDetail")
  32. }, {
  33. path: "*", redirect: "/index/rage"
  34. }],
  35. // 让每个页面都滚动到顶部,改变模式为mode: history
  36. scrollBehavior (to, from, savedPosition) {
  37. if (savedPosition) {
  38. return savedPosition
  39. } else {
  40. return { x: 0, y: 0 }
  41. }
  42. }
  43. })

vuex部分

这部分,主要是歌曲这一块,因为不同的页面有不同的使用到了歌曲信息,把把这部分数据放到vuex中做统一的数据处理!
sotre/index.js

</>复制代码

  1. const store = new Vuex.Store({
  2. state: {
  3. audio: {
  4. "id": 0,
  5. "name": "歌曲名称",
  6. "singer": "演唱者",
  7. "albumPic": "/static/player-bar.png",
  8. "location": "",
  9. "album": ""
  10. },
  11. lyric: "正在加载中。。",
  12. currentIndex: 0, // 当前播放的歌曲位置
  13. playing: false, // 是否正在播放
  14. loading: false, // 是否正在加载中
  15. showDetail: false,
  16. songList: [], // 播放列表
  17. currentTime: 0,
  18. tmpCurrentTime: 0,
  19. durationTime: 0,
  20. bufferedTime: 0,
  21. change: false // 判断是更改的时间还是播放的时间
  22. },
  23. getters: {
  24. audio: state => state.audio,
  25. playing: state => state.playing,
  26. loading: state => state.loading,
  27. showDetail: state => state.showDetail,
  28. durationTime: state => state.durationTime,
  29. currentIndex: state => state.currentIndex,
  30. bufferedTime: state => state.bufferedTime,
  31. tmpCurrentTime: state => state.tmpCurrentTime,
  32. songList: state => state.songList,
  33. change: state => state.change,
  34. currentTime: state => state.currentTime,
  35. prCurrentTime: state => {
  36. return state.currentTime / state.durationTime * 100
  37. },
  38. prBufferedTime: state => {
  39. return state.bufferedTime / state.durationTime * 100
  40. }
  41. },
  42. mutations: {
  43. play (state) {
  44. state.playing = true
  45. },
  46. pause (state) {
  47. state.playing = false
  48. },
  49. toggleDetail (state) {
  50. state.showDetail = !state.showDetail
  51. },
  52. setAudio (state) {
  53. state.audio = state.songList[state.currentIndex - 1]
  54. },
  55. setAudioIndex (state, index) {
  56. state.audio = state.songList[index]
  57. state.currentIndex = index + 1
  58. },
  59. removeAudio (state, index) {
  60. state.songList.splice(index, 1)
  61. state.audio = state.songList[index - 1]
  62. state.currentIndex = state.currentIndex - 1
  63. if (state.songList.length === 0) {
  64. state.audio = {
  65. "id": 0,
  66. "name": "歌曲名称",
  67. "singer": "演唱者",
  68. "albumPic": "/static/player-bar.png",
  69. "location": "",
  70. "album": ""
  71. }
  72. state.playing = false
  73. }
  74. },
  75. setChange (state, flag) {
  76. state.change = flag
  77. },
  78. setLocation (state, location) {
  79. state.audio.location = location
  80. },
  81. updateCurrentTime (state, time) {
  82. state.currentTime = time
  83. },
  84. updateDurationTime (state, time) {
  85. state.durationTime = time
  86. },
  87. updateBufferedTime (state, time) {
  88. state.bufferedTime = time
  89. },
  90. changeTime (state, time) {
  91. state.tmpCurrentTime = time
  92. },
  93. openLoading (state) {
  94. state.loading = true
  95. },
  96. closeLoading (state) {
  97. state.loading = false
  98. },
  99. resetAudio (state) {
  100. state.currentTime = 0
  101. },
  102. playNext (state) { // 播放下一曲
  103. state.currentIndex++
  104. if (state.currentIndex > state.songList.length) {
  105. state.currentIndex = 1
  106. }
  107. state.audio = state.songList[state.currentIndex - 1]
  108. },
  109. playPrev (state) { // 播放上一曲
  110. state.currentIndex--
  111. if (state.currentIndex < 1) {
  112. state.currentIndex = state.songList.length
  113. }
  114. state.audio = state.songList[state.currentIndex - 1]
  115. },
  116. addToList (state, item) {
  117. var flag = false
  118. state.songList.forEach(function (element, index) { // 检测歌曲重复
  119. if (element.id === item.id) {
  120. flag = true
  121. state.currentIndex = index + 1
  122. }
  123. })
  124. if (!flag) {
  125. state.songList.push(item)
  126. state.currentIndex = state.songList.length
  127. }
  128. },
  129. setLrc (state, lrc) {
  130. state.lyric = lrc
  131. }
  132. },
  133. // 异步的数据操作
  134. actions: {
  135. getSong ({commit, state}, id) {
  136. commit("openLoading")
  137. Axios.get(api.getSong(id)).then(res => {
  138. // 统一数据模型,方便后台接口的改变
  139. var url = res.data.data[0].url
  140. commit("setAudio")
  141. commit("setLocation", url)
  142. })
  143. },
  144. getLrc ({commit, state}, id) {
  145. commit("setLrc", "[txt](加载中。。。")
  146. Axios.get(api.getLrc(id)).then(res => {
  147. // 1、先判断是否有歌词
  148. if (res.data.nolyric) {
  149. commit("setLrc", "[txt](⊙0⊙) 暂无歌词")
  150. } else {
  151. console.log(res.data.lrc.lyric)
  152. commit("setLrc", res.data.lrc.lyric)
  153. }
  154. })
  155. }
  156. }
  157. })

</>复制代码

  1. 最后上点项目截图

github项目地址:https://github.com/javaSwing/NeteaseCloudWebApp

目前只完成app歌单部分,也是最核心的部分。这个项目会一直更新!如果觉的不错就给个star吧

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88138.html

相关文章

  • HTML+CSS+JAVASCRIPT 高仿低配网页版网易音乐播放器

    摘要:高仿低配网页版网易云音乐播放器前言没有使用任何框架,只是想用最简单纯的代码实现下前台后台是参考网上的例子写的,代码是在的基础上重新写的还有她的姊妹篇网易云音乐移动端,请查看这里写在前头的话鄙人野生前端一只,专业,自学前端已经一年多了 HTML+CSS+JAVASCRIPT 高仿低配网页版网易云音乐播放器 showImg(https://segmentfault.com/img/remo...

    RaoMeng 评论0 收藏0
  • HTML+CSS+JAVASCRIPT 高仿低配网页版网易音乐播放器

    摘要:高仿低配网页版网易云音乐播放器前言没有使用任何框架,只是想用最简单纯的代码实现下前台后台是参考网上的例子写的,代码是在的基础上重新写的还有她的姊妹篇网易云音乐移动端,请查看这里写在前头的话鄙人野生前端一只,专业,自学前端已经一年多了 HTML+CSS+JAVASCRIPT 高仿低配网页版网易云音乐播放器 showImg(https://segmentfault.com/img/remo...

    sean 评论0 收藏0
  • 如何能低成本地快速获取大量目标户,而不是与竞争对手持久战?

    摘要:如何能低成本地快速获取大量目标用户,而不是与竞争对手持久战这在如今的互联网上并不常见。产品经理需要寻找那些对产品价值最大的需求,而低成本地快速获取大量目标用户,就是重要的思考路径之一。 此文已由作者王诗沐授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验。 如何能低成本地快速获取大量目标用户,而不是与竞争对手持久战? 这在如今的互联网上并不常见。现在,大部分行业都已...

    sorra 评论0 收藏0

发表评论

0条评论

894974231

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<