前言:以下內容均為學習慕課網高級實戰課程的實踐爬坑筆記。
項目github地址:https://github.com/66Web/ljq_vue_music,歡迎Star。
![]() |
![]() |
| 歌曲列表 | 收藏歌曲 |
| 一、用戶個人中心開發 |
基礎功能開發
- 設置路由跳轉
- 在components->user-center目錄下:創建user-center.vue
- router->index.js中配置路由:
{ path: '/user', component: UserCenter } - m-header.vue中通過<router-link>實現跳轉
<router-link tp="/user" class="mine" tag="div"> <i class="icon-mine"></i> </router-link>
- 應用Switches組件
- 引用注冊並使用
<switches @switch="switchItem" :switches="switches" :currentIndex="currentIndex"></switches> - 定義data數據綁定
data() { return { currentIndex: 0, switches: [ {name: '我喜歡的'}, {name: '最近聽的'} ] } } - 定義方法將獲得的index賦值給currentIndex
switchItem(index) { this.currentIndex = index }
- 返回上一級路由
- 給按鈕添加點擊事件
@click="back" - 定義方法通過router.back返回
back(){ this.$router.back() //回退到上一級路由 }
收藏列表開發
- Vuex數據配置
- states.js中:添加數據
favoriteList: [] - mutation-types.js中:定義事件類型常量
export const SET_FAVORITE_LIST = 'SET_FAVORITE_LIST' - mutations.js中:定義修改方法
[types.SET_FAVORITE_LIST](state, list){ state.favoriteList = list } - getters.js中:設置數據映射
export const favoriteList = state => state.favoriteList
- catch.js中操作storage
- 定義本地緩存的Key和最大存儲歌曲值
const FAVORITE_KEY = '_favorite_' const FAVORITE_MAX_LENGTH = 200 - 保存歌曲到本地緩存
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 } - 從本地緩存中刪除歌曲
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 } - 從本地緩存中獲取全部歌曲
export function loadFavorite() { return storage.get(FAVORITE_KEY, []) }
- actions.js中:封裝方法
- 同時保存到Vuex和本地緩存
export const saveFavoriteList = function ({commit}, song) { commit(types.SET_FAVORITE_LIST, saveFavorite(song)) } - 同時從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中添加收藏歌曲需要的共享邏輯
- 通過mapGetters獲得已收藏的歌曲數據:'favoriteList'
- 抽象出一個方法判斷所選歌曲是否在已收藏的歌曲數據中
isFavorite(song){ const index = this.favoriteList.findIndex((item) => { return item.id === song.id }) return index > -1 //如果index > -1 isFavorite 返回true } - 定義方法依據所選歌曲是否為已收藏的歌曲,取反改變icon樣式
getFavoriteIcon(song){ if(this.isFavorite(song)){ return 'icon-favorite' } return 'icon-not-favorite' } - 定義方法調用通過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中渲染收藏列表和播放歷史列表
- 布局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> - 通過mapGetters獲取收藏歌曲數據和播放歷史數據
computed: { ...mapGetters([ 'favoriteList', 'playHistory' ]) } - 定義方法,調用通過mapActions獲取到的insertSong方法,將song實例化之后插入
selectSong(song) { this.insertSong(new Song(song)) }, ...mapActions([ 'insertSong' ])
剩余功能開發
- 隨機播放全部功能實現
- 給按鈕添加點擊事件,通過mapActions獲取到randomPlay方法
<div ref="playBtn" class="play-btn" @click="random"> - 定義方法,判斷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組件的應用
- 布局DOM:
<div class="no-result-wrapper" v-if="noResult"> <no-result :title="noResultDesc"></no-result> </div> - 顯示條件和顯示提示內容都需要動態綁定計算屬性,判斷currentIndex
noResult() { if(this.currentIndex === 0) { return !this.favoriteList.length }else{ return !this.playHistory.length } }, noResultDesc() { if(this.currentIndex === 0) { return '暫無收藏歌曲' }else{ return '你還沒有聽過歌曲' } }
- 優化:當列表中無數據,點擊隨機播放全部時,rendom()不執行任何操作
if(list.length === 0) { return }
| 二、性能優化 |
- 坑:快速的點擊播放暫停歌曲,發現歌曲和歌詞還在播放
- 原因:player.vue中watch currentSong會做一些清理操作,並在1s內開啟play()播放;此時如果很快的切換歌曲切換播放狀態,調用ready()和watch playing中的pause()會在1s內執行完;雖然看起來pause執行了,但1s過去之后,又會重新開啟play()
- 解決:在每次setTimeout前清理掉舊的timer,保證只有一個timer;同時修改ready的觸發事件為play,保證ready()和pause()一定發生在play()后
- 區別:
①事件canplay -- 當瀏覽器可以播放音頻/視頻時
②事件play -- 當音頻/視頻已開始或不再暫停時
- 注: 這里添加clearTimeout(this.timer)時總報timeout.close is not function,top-tip中使用過清理timer,沒有問題,這里就不知道為什么了,待解決!
- 坑:快速切換歌曲時,歌詞的播放異常
- 原因:異步時機問題 -- setTImeout執行1s的操作中play()是一個同步的動作,而getLyric()是異步的操作;在異步獲得回調時,有可能又切換到了下一首歌,這時之前的歌會在new一次,相當於new了兩次,會有多個歌詞同時存在
- 解決:在異步獲得回調后先判斷currentSong.lyric是否改變了,如果改變了不為layric,不執行任何操作
if(this.currentSong.lyric !== lyric){ return }
- 坑:當前歌曲只有一首歌時,切換下一首,不會再觸發ready()了
- 原因:next()中當列表長度為1時,會調用loop(),后面的ready標志位會一直為false,redy()也就不會再觸發了
- 解決:在next()和prev()中,如果調用了loop()就直接return,不再修改標志位為false
| 三、項目打包及VConsole的使用 |
編譯打包
npm run build
VConsole的使用
- 安裝
- 在頁面引入一個JS文件:下載地址
- 使用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>
注:項目來自慕課網


