前言:本身學習VUEJS也一段時間,但一直沒有作出來一東西。我本身一直喜歡用網易雲音樂app,因而乎就作了這個app。javascript
vue全家桶 (vue vue-router vuex)html
axiosvue
Muse-UI(一個基於Vue2.x的material design 風格UI框架)java
我以前學習JS的時候對Html5 audio研究過,也寫過一些例子,那時的功能並非很全面。在寫這個程序以前,我好好的查閱了當前的HTML5中的audio標籤,發現園子上一位園友總結的很不錯(這裏)。因而就先把網易雲音樂最基本的功能實現,歌單部分(這也是我喜歡網易雲音樂的緣由這一),而後實現音樂的上一曲、下一曲,播放、暫停。列表功能。ios
後臺採用.net作爲後臺提供系統請求所用的API(源碼),原理很簡單就是用.net假裝成一個客戶端去訪問網易雲音樂的API而後,把返回的json數據轉發出來。同時服務端作下跨域處理。git
核心代碼:github
/// <summary> /// 請求網易雲音樂接口 /// </summary> /// <typeparam name="T">要請求的接口類型</typeparam> /// <param name="config">要請求的接口類型的對象</param> /// <returns>請求結果(JSON)</returns> public static string Request<T>(T config) where T : RequestData, new() { // 請求URL string requestURL = config.Url; // 將數據包對象轉換成QueryString形式的字符串 string @params = config.FormData.ParseQueryString(); bool isPost = config.Method.Equals("post", StringComparison.CurrentCultureIgnoreCase); if (!isPost) { // get方式 拼接請求url string sep = requestURL.Contains('?') ? "&" : "?"; requestURL += sep + @params; } HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestURL); req.Accept = "*/*"; req.Headers.Add("Accept-Language", "zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4"); // 若是服務端啓用了GZIP,那麼下面必須解壓,不然一直亂碼。 // 參見:http://www.crifan.com/set_accept_encoding_header_to_gzip_deflate_return_messy_code/ req.Headers.Add("Accept-Encoding", "gzip,deflate,sdch"); req.ContentType = "application/x-www-form-urlencoded"; req.KeepAlive = true; req.Host = "music.163.com"; req.Referer = "http://music.163.com/search/"; 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"; // 設置cookies req.Headers["Cookie"] = "appver=1.5.2"; req.Method = config.Method; req.AutomaticDecompression = DecompressionMethods.GZip; if (isPost) { // 寫入post請求包 byte[] formData = Encoding.UTF8.GetBytes(@params); // 設置HTTP請求頭 參考:https://github.com/darknessomi/musicbox/blob/master/NEMbox/api.py req.GetRequestStream().Write(formData, 0, formData.Length); } // 發送http請求 並讀取響應內容返回 return new StreamReader(req.GetResponse().GetResponseStream(), Encoding.GetEncoding("UTF-8")).ReadToEnd(); }
項目結構vue-router
├── index.html ├── main.js ├── api │ └── ... # 抽取出API請求 ├── components │ ├── playBar.vue │ └── ... └── store │ └── index.js # 整個項目的vuex部分 └── router │ └── router.js # 整個項目的路由 └── utils # 一些工具類模塊 │ └── views # 項目中的一些route-view
說項目的路由以前,先來看一張效果圖vuex
對於整個項目來講:視圖區別在於頂部導航,下面的bar的是否出來取決於,當前系統列表中是否有歌曲,若是有就會出現。shell
router.js核心部分
const router = new VueRouter({ mode: 'history', routes: [{ path: '/index', component: require('../views/index'), children: [ { path: 'rage', component: require('../views/rage') }, { path: 'songList', component: require('../views/songList') }, { path: 'leaderBoard', component: require('../views/leaderBoard') }, { path: 'hotSinger', component: require('../views/hotSinger') } ] }, { name: 'playerDetail', path: '/playerDetail/:id', component: require('../views/playerDetail') }, { path: '/playListDetail/:id', name: 'playListDetail', component: require('../views/playListDetail') }, { path: '*', redirect: '/index/rage' }], // 讓每一個頁面都滾動到頂部,改變模式爲mode: history scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } } })
vuex部分
這部分,主要是歌曲這一塊,由於不一樣的頁面有不一樣的使用到了歌曲信息,把把這部分數據放到vuex中作統一的數據處理!
sotre/index.js
const store = new Vuex.Store({ state: { audio: { 'id': 0, 'name': '歌曲名稱', 'singer': '演唱者', 'albumPic': '/static/player-bar.png', 'location': '', 'album': '' }, lyric: '正在加載中。。', currentIndex: 0, // 當前播放的歌曲位置 playing: false, // 是否正在播放 loading: false, // 是否正在加載中 showDetail: false, songList: [], // 播放列表 currentTime: 0, tmpCurrentTime: 0, durationTime: 0, bufferedTime: 0, change: false // 判斷是更改的時間仍是播放的時間 }, getters: { audio: state => state.audio, playing: state => state.playing, loading: state => state.loading, showDetail: state => state.showDetail, durationTime: state => state.durationTime, currentIndex: state => state.currentIndex, bufferedTime: state => state.bufferedTime, tmpCurrentTime: state => state.tmpCurrentTime, songList: state => state.songList, change: state => state.change, currentTime: state => state.currentTime, prCurrentTime: state => { return state.currentTime / state.durationTime * 100 }, prBufferedTime: state => { return state.bufferedTime / state.durationTime * 100 } }, mutations: { play (state) { state.playing = true }, pause (state) { state.playing = false }, toggleDetail (state) { state.showDetail = !state.showDetail }, setAudio (state) { state.audio = state.songList[state.currentIndex - 1] }, setAudioIndex (state, index) { state.audio = state.songList[index] state.currentIndex = index + 1 }, removeAudio (state, index) { state.songList.splice(index, 1) state.audio = state.songList[index - 1] state.currentIndex = state.currentIndex - 1 if (state.songList.length === 0) { state.audio = { 'id': 0, 'name': '歌曲名稱', 'singer': '演唱者', 'albumPic': '/static/player-bar.png', 'location': '', 'album': '' } state.playing = false } }, setChange (state, flag) { state.change = flag }, setLocation (state, location) { state.audio.location = location }, updateCurrentTime (state, time) { state.currentTime = time }, updateDurationTime (state, time) { state.durationTime = time }, updateBufferedTime (state, time) { state.bufferedTime = time }, changeTime (state, time) { state.tmpCurrentTime = time }, openLoading (state) { state.loading = true }, closeLoading (state) { state.loading = false }, resetAudio (state) { state.currentTime = 0 }, playNext (state) { // 播放下一曲 state.currentIndex++ if (state.currentIndex > state.songList.length) { state.currentIndex = 1 } state.audio = state.songList[state.currentIndex - 1] }, playPrev (state) { // 播放上一曲 state.currentIndex-- if (state.currentIndex < 1) { state.currentIndex = state.songList.length } state.audio = state.songList[state.currentIndex - 1] }, addToList (state, item) { var flag = false state.songList.forEach(function (element, index) { // 檢測歌曲重複 if (element.id === item.id) { flag = true state.currentIndex = index + 1 } }) if (!flag) { state.songList.push(item) state.currentIndex = state.songList.length } }, setLrc (state, lrc) { state.lyric = lrc } }, // 異步的數據操做 actions: { getSong ({commit, state}, id) { commit('openLoading') Axios.get(api.getSong(id)).then(res => { // 統一數據模型,方便後臺接口的改變 var url = res.data.data[0].url commit('setAudio') commit('setLocation', url) }) }, getLrc ({commit, state}, id) { commit('setLrc', '[txt](加載中。。。') Axios.get(api.getLrc(id)).then(res => { // 一、先判斷是否有歌詞 if (res.data.nolyric) { commit('setLrc', '[txt](⊙0⊙) 暫無歌詞') } else { console.log(res.data.lrc.lyric) commit('setLrc', res.data.lrc.lyric) } }) } } })
最後上點項目截圖
github項目地址:https://github.com/javaSwing/NeteaseCloudWebApp
目前只完成app歌單部分,也是最核心的部分。這個項目會一直更新!若是覺的不錯就給個star吧