• 【音乐App】—— Vue-music 项目学习笔记:用户个人中心开发


    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记。

    项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star。


    歌曲列表 收藏歌曲
    一、用户个人中心开发

           基础功能开发

    • 设置路由跳转
    1. 在components->user-center目录下:创建user-center.vue
    2. router->index.js中配置路由:
      {
         path: '/user',
         component: UserCenter
      }
    3. m-header.vue中通过<router-link>实现跳转
      <router-link tp="/user" class="mine" tag="div">
          <i class="icon-mine"></i>
      </router-link>
    • 应用Switches组件
    1. 引用注册并使用
      <switches @switch="switchItem" :switches="switches" 
                :currentIndex="currentIndex"></switches>
    2. 定义data数据绑定
      data() {
         return {
            currentIndex: 0,
            switches: [
               {name: '我喜欢的'},
               {name: '最近听的'}
            ]
        }
      }
    3. 定义方法将获得的index赋值给currentIndex
      switchItem(index) {
          this.currentIndex = index
      }
    • 返回上一级路由
    1. 给按钮添加点击事件
      @click="back"
    2. 定义方法通过router.back返回
      back(){
          this.$router.back() //回退到上一级路由
      }

           收藏列表开发

    • Vuex数据配置
    1. states.js中:添加数据
      favoriteList: []
    2. mutation-types.js中:定义事件类型常量
      export const SET_FAVORITE_LIST = 'SET_FAVORITE_LIST'
    3. mutations.js中:定义修改方法
      [types.SET_FAVORITE_LIST](state, list){
          state.favoriteList = list
      }
    4. getters.js中:设置数据映射
      export const favoriteList = state => state.favoriteList
    • catch.js中操作storage
    1. 定义本地缓存的Key和最大存储歌曲值
      const FAVORITE_KEY = '_favorite_'
      const FAVORITE_MAX_LENGTH = 200
    2. 保存歌曲到本地缓存
      export function saveFavorite(song) {
          let songs = storage.get(FAVORITE_KEY, [])
          insertArray(songs, song, (item) => {
                return song.id === item.id
          }, FAVORITE_MAX_LENGTH)
          storage.set(FAVORITE_KEY, songs)
          return songs 
      }
    3. 从本地缓存中删除歌曲
      export function deleteFavorite(song) {
          let songs = storage.get(FAVORITE_KEY, []) 
          deleteFromArray(songs, (item) => { 
               return song.id === item.id
         })
         storage.set(FAVORITE_KEY, songs) 
         return songs
      }
    4. 从本地缓存中获取全部歌曲
      export function loadFavorite() {
          return storage.get(FAVORITE_KEY, []) 
      }
    • actions.js中:封装方法
    1. 同时保存到Vuex和本地缓存
      export const saveFavoriteList = function ({commit}, song) {
          commit(types.SET_FAVORITE_LIST, saveFavorite(song))
      }
    2. 同时从Vuex和本地缓存删除
      export const deleteFavoriteList = function ({commit}, song) {
          commit(types.SET_FAVORITE_LIST, deleteFavorite(song))
      }
    • states.js中修改初始数据为本地缓存数据
      favoriteList: loadFavorite()
    • player.vue中修改按钮,动态绑定class,监听点击事件
      <i class="icon" @click="toggleFavorite(currentSong)" 
         :class="getFavoriteIcon(currentSong)">
    • mixin.js中在playerMixin中添加收藏歌曲需要的共享逻辑
    1. 通过mapGetters获得已收藏的歌曲数据:'favoriteList'
    2. 抽象出一个方法判断所选歌曲是否在已收藏的歌曲数据中
      isFavorite(song){
         const index = this.favoriteList.findIndex((item) => {
             return item.id === song.id
         })
         return index > -1 //如果index > -1 isFavorite 返回true
      }
    3. 定义方法依据所选歌曲是否为已收藏的歌曲,取反改变icon样式
      getFavoriteIcon(song){
         if(this.isFavorite(song)){
            return 'icon-favorite'
         } 
         return 'icon-not-favorite'
      }
    4. 定义方法调用通过mapActions引用的action,同上取反进行保存或删除
      toggleFavorite(song){
          if(this.isFavorite(song)){
             this.deleteFavoriteList(song)
          }else{
             this.saveFavoriteList(song)
          }
      }
    • playlist.vue中添加数据映射和点击事件
      <span class="like" @click.stop="toggleFavorite(item)">
          <i :class="getFavoriteIcon(item)"></i>
      </span>
    • usercenter.vue中渲染收藏列表和播放历史列表
    1. 布局DOM: 同add-song.vue
      <scroll ref="favoriteList" class="list-scroll" v-if="currentIndex===0" 
                :data="favoriteList">
              <div class="list-inner">
                  <song-list :songs="favoriteList" @select="selectSong"></song-list>
              </div>
      </scroll>
      <scroll ref="playList" class="list-scroll" v-if="currentIndex===1" 
                :data="playHistory">
              <div class="list-inner">
                  <song-list :songs="playHistory" @select="selectSong"></song-list>
              </div>
      </scroll>
    2. 通过mapGetters获取收藏歌曲数据和播放历史数据
      computed: {
         ...mapGetters([
            'favoriteList',
            'playHistory'
        ])
      }
    3. 定义方法,调用通过mapActions获取到的insertSong方法,将song实例化之后插入
      selectSong(song) {
         this.insertSong(new Song(song)) 
      },
      ...mapActions([
         'insertSong'
      ])

           剩余功能开发

    • 随机播放全部功能实现
    1. 给按钮添加点击事件,通过mapActions获取到randomPlay方法
      <div ref="playBtn" class="play-btn" @click="random">
    2. 定义方法,判断currentIndex获取对应list,通过实例化处理传入action
      random(){
          let list = this.currentIndex === 0 ? this.favoriteList : this.playHistory
          //这时list还不是实例,需要遍历list进行实例化
          list = list.map((song) => {
               return new Song(song)
          }) 
          this.randomPlay({
               list
          })
      }
    • 播放器底部自适应
      import {playlistMixin} from '@/common/js/mixin'
      
      mixins:[playlistMixin],
      
      handlePlaylist(playlist){
          const bottom = playlist.length > 0 ? '60px' : ''
          this.$refs.listWrapper.style.bottom = bottom
          //判断列表DOM存在后再执行refresh
          this.$refs.favoriteList && this.$refs.favoriteList.refresh()
          this.$refs.playList && this.$refs.playList.refresh()
      }
    • no-result组件的应用
    1. 布局DOM:
      <div class="no-result-wrapper" v-if="noResult">
           <no-result :title="noResultDesc"></no-result>
      </div>
    2. 显示条件和显示提示内容都需要动态绑定计算属性,判断currentIndex
      noResult() {
          if(this.currentIndex === 0) {
             return !this.favoriteList.length
          }else{
             return !this.playHistory.length
          }
      },
      noResultDesc() {
          if(this.currentIndex === 0) {
             return '暂无收藏歌曲'
          }else{
             return '你还没有听过歌曲'
          }
      } 
    3. 优化:当列表中无数据,点击随机播放全部时,rendom()不执行任何操作
      if(list.length === 0) {
         return
      }
    二、性能优化
    • 坑:快速的点击播放暂停歌曲,发现歌曲和歌词还在播放
    1. 原因:player.vue中watch currentSong会做一些清理操作,并在1s内开启play()播放;此时如果很快的切换歌曲切换播放状态,调用ready()和watch playing中的pause()会在1s内执行完;虽然看起来pause执行了,但1s过去之后,又会重新开启play() 
    2. 解决:在每次setTimeout前清理掉旧的timer,保证只有一个timer;同时修改ready的触发事件为play,保证ready()和pause()一定发生在play()后
    3. 区别:

      ①事件canplay -- 当浏览器可以播放音频/视频时

      ②事件play -- 当音频/视频已开始或不再暂停时

    4. 注: 这里添加clearTimeout(this.timer)时总报timeout.close is not function,top-tip中使用过清理timer,没有问题,这里就不知道为什么了,待解决!
    • 坑:快速切换歌曲时,歌词的播放异常
    1. 原因:异步时机问题 -- setTImeout执行1s的操作中play()是一个同步的动作,而getLyric()是异步的操作;在异步获得回调时,有可能又切换到了下一首歌,这时之前的歌会在new一次,相当于new了两次,会有多个歌词同时存在
    2. 解决:在异步获得回调后先判断currentSong.lyric是否改变了,如果改变了不为layric,不执行任何操作
      if(this.currentSong.lyric !== lyric){
         return
      } 
    • 坑:当前歌曲只有一首歌时,切换下一首,不会再触发ready()了
    1. 原因:next()中当列表长度为1时,会调用loop(),后面的ready标志位会一直为false,redy()也就不会再触发了
    2. 解决:在next()和prev()中,如果调用了loop()就直接return,不再修改标志位为false
    三、项目打包及VConsole的使用

           编译打包 

    npm run build

           VConsole的使用  

    • 安装
    1. 在页面引入一个JS文件:下载地址
    2. 使用npm安装
      npm install vconsole
    • 使用webpack,然后在js代码中应用VConsole
      import VConsole from 'vconsole/dist/vconsole.min.js' //引入vconsole
      let vConsole = new VConsole() // 初始化
      或者找到这个模块下面的 dist/vconsole.min.js ,然后复制到自己的项目中
      <head>
          <script src="dist/vconsole.min.js"></script>
      </head>
      <!--建议在 `<head>` 中引入哦~ -->
      <script>
        // 初始化
        var vConsole = new VConsole();
        console.log('VConsole is cool');
      </script>

    注:项目来自慕课网

  • 相关阅读:
    JavaScript的语法、数据类型、基本算数和逻辑运算操作
    ES6之常用开发知识点:入门(一)
    ES6中map数据结构
    VUE路径问题

    JS简易计算器的实现,以及代码的优化
    格雷编码
    H5网页布局+css代码美化
    jQuery---五角星评分案例
    Ajax工作原理及优缺点
  • 原文地址:https://www.cnblogs.com/ljq66/p/10184159.html
Copyright © 2020-2023  润新知