Foreword: I have studied VUEJS for some time, but I haven't made anything. I have always liked to use Netease cloud music app, so I made this app.
technology stack
-
Vue family barrel (vue vue-router vuex)
-
axios
-
Muse-UI (a Vue2.x-based material design style UI framework)
Analysis of Function and Thought
When I studied JS, I was right. HTML5 audio studied and wrote some examples, but the functions were not very comprehensive at that time. Before writing this program, I checked the current one. Html5 In the audio label, I found that a gardener on the garden summed up very well.( Here ) So we first realized the basic function of Netease cloud music, the song list part (which is the reason why I like Netease cloud music), and then realized the last song, the next song, play, pause. List function.
Backstage
Background uses. net as the API for backstage system requests( Source code The principle is very simple. net disguises itself as a client to access the API of Netease Cloud Music and then forwards the returned json data. At the same time, the server does cross-domain processing.
Core code:
/// <summary>
/// Request Netease Cloud Music Interface
/// </summary>
/// <typeparam name="T">The type of interface to request</typeparam>
/// <param name="config">Objects of the interface type to request</param>
/// <returns>Request results(JSON)</returns>
public static string Request<T>(T config) where T : RequestData, new()
{
// Request URL
string requestURL = config.Url;
// Converting Packet Objects into Strings in QueryString Form
string @params = config.FormData.ParseQueryString();
bool isPost = config.Method.Equals("post", StringComparison.CurrentCultureIgnoreCase);
if (!isPost)
{
// get splicing request 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");
// If GZIP is enabled on the server side, then the decompression must be done below, otherwise the code will be scrambled all the time.
// See: 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";
// Setting cookies
req.Headers["Cookie"] = "appver=1.5.2";
req.Method = config.Method;
req.AutomaticDecompression = DecompressionMethods.GZip;
if (isPost)
{
// Write to post request package
byte[] formData = Encoding.UTF8.GetBytes(@params);
// Setting HTTP request header reference: https://github.com/darknessomi/music box/blob/master/NEMbox/api.py
req.GetRequestStream().Write(formData, 0, formData.Length);
}
// Send an http request and read the response and return it
return new StreamReader(req.GetResponse().GetResponseStream(), Encoding.GetEncoding("UTF-8")).ReadToEnd();
}
vuejs section
Project structure
├── index.html
├── main.js
├── api
│ └── ... # Extract API requests
├── components
│ ├── playBar.vue
│ └── ...
└── store
│ └── index.js # vuex part of the whole project
└── router
│ └── router.js # Routing of the whole project
└── utils # Some Tool Class Modules
│
└── views # Some route-view s in the project
Before you talk about the routing of a project, take a look at an effect map.
For the project as a whole, the view is different from the top navigation. Whether the bar below comes out depends on whether there are songs in the current system list, and if there are, they will appear.
router.js Core
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'
}],
// Let each page scroll to the top and change the pattern to mode: history
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
})
vuex part
This part, mainly the song, because different pages have different uses of song information, put this part of data into vuex to do unified data processing! sotre/index.js
const store = new Vuex.Store({
state: {
audio: {
'id': 0,
'name': 'Name of song',
'singer': 'Singer',
'albumPic': '/static/player-bar.png',
'location': '',
'album': ''
},
lyric: 'Loading.',
currentIndex: 0, // Location of the song currently playing
playing: false, // Is it playing now?
loading: false, // Is it loading?
showDetail: false,
songList: [], // Playlist
currentTime: 0,
tmpCurrentTime: 0,
durationTime: 0,
bufferedTime: 0,
change: false // Judge whether it's a change of time or a playback time
},
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': 'Name of song',
'singer': '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) { // Play the next song
state.currentIndex++
if (state.currentIndex > state.songList.length) {
state.currentIndex = 1
}
state.audio = state.songList[state.currentIndex - 1]
},
playPrev (state) { // Play the last song
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) { // Detecting song repetition
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
}
},
// Asynchronous data manipulation
actions: {
getSong ({commit, state}, id) {
commit('openLoading')
Axios.get(api.getSong(id)).then(res => {
// Unified data model to facilitate the change of background interface
var url = res.data.data[0].url
commit('setAudio')
commit('setLocation', url)
})
},
getLrc ({commit, state}, id) {
commit('setLrc', '[txt](Loading...')
Axios.get(api.getLrc(id)).then(res => {
// 1. Judge whether there are lyrics first
if (res.data.nolyric) {
commit('setLrc', '[txt](⊙0⊙) No lyrics yet')
} else {
console.log(res.data.lrc.lyric)
commit('setLrc', res.data.lrc.lyric)
}
})
}
}
})
Final point project screenshot
github project address: https://github.com/javaSwing/NeteaseCloudWebApp
https://github.com/javaSwing
At present, it only completes the app song list part, which is also the core part. This project will be updated all the time! If you think it's good, give it a star t.