Preface: I learn VUEJS for a period of time, but I have not made a thing. I always like to use netease cloud music APP, so I made this app.

Technology stack

  • Vue (Vue vuE-Router Vuex)

  • axios

  • Muse-ui (a Material Design Style UI framework based on Ve2. X)

Analysis of functions and ideas

I studied Html5 Audio and wrote some examples when I was learning JS. At that time, the functions were not very comprehensive. Before writing this application, I took a good look at the audio tag in current HTML5 and found that a garden friend summed it up well (here). Therefore, the most basic functions of netease Cloud music will be realized first, namely the song list part (which is also the reason why I like netease Cloud Music), and then the music will be played and paused. List function.

The background

.net is used as the background to provide system request API(source code), the principle is very simple is to use.NET disguised as a client to access netease cloud music API and then return the JSON data forward out. At the same time, the server performs 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">Object of the type of interface to request</param>
/// <returns>Request result (JSON)</returns>
public static string Request<T>(T config) where T : RequestData, new()
{
    / / request URL
    string requestURL = config.Url;
    // Convert the packet object to a QueryString
    string @params = config.FormData.ParseQueryString();
    bool isPost = config.Method.Equals("post", StringComparison.CurrentCultureIgnoreCase);

    if(! isPost) {// Splice request URL in get mode
        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, useful - TW; Q = 0.4");
    // If the server is enabled with GZIP, then the following must be decompressed, otherwise the garbled characters will continue.
    / / 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";            
    / / set cookies
    req.Headers["Cookie"] = "Appver = 1.5.2";
    req.Method = config.Method;
    req.AutomaticDecompression = DecompressionMethods.GZip;
    if (isPost)
    {
        // Write the POST request package
        byte[] formData = Encoding.UTF8.GetBytes(@params);
        / / set the HTTP request header reference: https://github.com/darknessomi/musicbox/blob/master/NEMbox/api.py
        req.GetRequestStream().Write(formData, 0, formData.Length);
    }            
    // Send the HTTP request and read the response
    return new StreamReader(req.GetResponse().GetResponseStream(), Encoding.GetEncoding("UTF-8")).ReadToEnd();
}Copy the code

Vuejs part

The project structure



├ ─ ─index.html ├ ─ ─ main. Js ├ ─ ─ API │ └ ─ ─...Extract THE API request├── ├─ Bass exercises. │ ├─ ├─ bass exercises. └ ─ ─ store │ └ ─ ─index.js        # VuEX part of the whole project└── garbage │ ├ ─ garbage# Routing of the entire project└ ─ ─ utils# Some utility class modules│ └ ─ ─ views# Some Route-Views in the projectCopy the code

Before we talk about the route of the project, let’s take a look at a rendering

For the whole project: the view difference is the navigation at the top, and 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: The '*', redirect: '/index/rage'}].// Scroll to the top of each page and change the mode to mode: history
  scrollBehavior (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { x: 0, y: 0}}}})Copy the code

Vuex part

This part, mainly songs, because different pages have different use of song information, put this part of data into VUEX for unified data processing! sotre/index.js



const store = new Vuex.Store({
  state: {
    audio: {
      'id': 0.'name': 'Song Title'.'singer': 'singer'.'albumPic': '/static/player-bar.png'.'location': ' '.'album': ' '
    },
    lyric: 'Loading. ',
    currentIndex: 0.// The current position of the song
    playing: false.// Whether it is playing
    loading: false.// Whether the file is being loaded
    showDetail: false,
    songList: [],    // Playlist
    currentTime: 0,
    tmpCurrentTime: 0,
    durationTime: 0,
    bufferedTime: 0,
    change: false   // Determine whether it is the changed time or the 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': 'Song Title'.'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 previous 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) { // Detect 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, convenient background interface change
        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, check if there are lyrics
        if (res.data.nolyric) {
          commit('setLrc'.'[TXT](⊙0⊙))}else {
          console.log(res.data.lrc.lyric)
          commit('setLrc', res.data.lrc.lyric)
        }
      })
    }
  }
})Copy the code

Finally, click on the project screenshot

Github project address: github.com/javaSwing/N…

Currently, I have only completed the app playlist, which is also the core part. This project will be updated all the time! Give a star if you feel good