1. 程式人生 > 其它 >Vue3.0 + typescript 高仿網易雲音樂 WebApp

Vue3.0 + typescript 高仿網易雲音樂 WebApp

技術標籤:vuevue.jstypescriptjavascriptes6sass

Vue3.0 + typescript 高仿網易雲音樂 WebApp

前言

Vue3.0 的正式釋出,讓我心動不已,於是嘗試用 vue3 實現一個完整的專案,整個專案全部使用了 composition api, 相比較 options 方式,邏輯更加清晰,使用也更加靈活。

先貼上地址,喜歡的可以 star 一波
原始碼地址 : https://github.com/bianyingchun/vue3-music
這裡還有 react hooks 版本

專案介紹

本專案是使用 vue-cli 4.5 搭建的專案, vue-router 以及 vuex 均為 4.0 版本,以支援 composition api 的使用。使用 typescript 編寫完成。該專案的所有資料介面來自大佬 binaryify 的

網易雲音樂 NodeJS 版 API

本專案將會持續維護開發中,目前實現了以下模組:

滾動載入更多

這個專案中幾乎所有的列表頁都使用到了滾動載入更多的邏輯,於是我封裝了一個公共 hooks,以供複用

import _ from 'lodash'
import { Ref, onMounted, onUnmounted } from 'vue'
export function useLoadMore(
  refreshElm: Ref<null | HTMLElement>,
  loadData: () => any
) {
  let element: HTMLElement
  const
_loadMore = _.throttle(() => { const containerHeight = element.clientHeight const scrollTop = element.scrollTop const scrollHeight = element.scrollHeight if (containerHeight + scrollTop + 20 >= scrollHeight) { loadData() } }, 200) onMounted(() => { element =
refreshElm.value as HTMLElement element.addEventListener('scroll', _loadMore) }) onUnmounted(() => { element.removeEventListener('scroll', _loadMore) }) return {} }

主題切換

通過 css 變數和 sass mixin 的結合,實現了明暗主題的切換,當然你可以定義不同的主題。

@mixin css-var(
  $text,
  $text-reversal,
  $text-nav,
  $header-bg,
  $text-secondary,
  $primary,
  $link-bg,
  $body-bg,
  $module-bg,
  $border
) {
  #{--body-bg}: $body-bg;
  #{--text}: $text;
  #{--text-reversal}: $text-reversal;
  #{--text-nav}: $text-nav;
  #{--text-secondary}: $text-secondary;
  #{--primary}: $primary;
  #{--header-bg}: $header-bg;
  #{--link-bg}: $link-bg;
  #{--module-bg}: $module-bg;
  #{--border}: $border;
  #{--bg-reversal-opacity-9}: rgba($text, 0.9);
}
.light {
  @include css-var(
    $text: #333,
    $text-reversal: #fff,
    $text-nav: #5d5d5d,
    $text-secondary: #888,
    $primary: #d23931,
    //active
      $link-bg: #f34d3f,
    $header-bg: #da3d34,
    $body-bg: #f0f0f0,
    $module-bg: #fff,
    $border: #ccc
  );
}
.dark {
  @include css-var(
    $text: #fff,
    $text-reversal: #333,
    $text-nav: #d7d7d7,
    $text-secondary: #ccc,
    $primary: #f1423d,
    $link-bg: #414141,
    $header-bg: #414141,
    $body-bg: #262626,
    $module-bg: #2c2c2c,
    $border: #4e4a4a
  );
}
亮色主題 亮色主題

登入

通過登入以體驗更豐富的功能,如發表評論,點贊,關注使用者,歌手,收藏歌曲,建立歌單等。在未登入狀態下觸及到這些功能時會自動顯示登入介面,登入成功後會返回到當前頁面,重新整理當前使用者狀態。
為了共享使用者狀態,我將使用者狀態儲存到 store 中,並抽離出公共邏輯 useAuth.ts

export function useAuth(store: Store<GlobalState>) {
  const account = computed(() => store.state.auth.account);
  const profile = computed(() => store.state.auth.profile);
  const toggleLoginBox = (val: boolean) =>
    store.commit(`auth/${SET_LOGIN_VISIBLE}`, val);
  return {
    account,
    profile,
    toggleLoginBox,
  };
}
亮色主題

個人中心

在使用者登入成功,個人中心會顯示使用者資訊,以及個人建立和收藏的歌單, 使用者可以建立、刪除、編輯歌單、取消收藏的歌單。
建立歌單

播放器

播放器算是此專案中最核心的模組了。實現了一個播放器應該有的基本功能。

  • 列表播放
  • 插入歌曲
  • 切換歌曲
  • 從播放列表中刪除歌曲
  • 清空播放列表
  • 切換播放模式
  • 歌詞同步
  • 調整播放進度
  • 新增到歌單
    由於在多個頁面中使用到了播放歌曲的功能,我抽離出了公共邏輯 usePlayMusic.ts
export function usePlayMusic(store: Store<GlobalState>) {
  const playing = computed(() => store.state.player.playing);
  const currentSong = computed(() => store.getters["player/currentSong"]);
  function selectPlay(list: Track[], index: number) {
    store.dispatch("player/selectPlay", {
      list,
      index,
    });
  }
  function insertSong(song: Track) {
    store.dispatch("player/insertSong", song);
  }
  return {
    currentSong,
    insertSong,
    selectPlay,
    playing,
  };
}

在實現收藏歌曲到歌單時,考慮到這個功能多次被使用,且無需反覆建立,故設計為公共單例元件,並封裝成 hooks,方便呼叫。

import { ref, createApp, App, h } from "vue";
import Comp from "@/components/achive/fav-to-mix.vue";
import { useMylist } from "./usePlaylist";
import { useAuth } from "./useAuth";
import { Track, Playlist } from "@/types";
import store from "@/store";

let favToMixVm: App | null = null;

const show = ref(false);
function hide() {
  show.value = false;
}

export function favTrackToMix(track: Track) {
  const { account, toggleLoginBox } = useAuth(store);
  if (!account.value) return toggleLoginBox(true);
  show.value = true;
  const { likelist, createdList, addTrack } = useMylist(store);
  const list = likelist.value
    ? [likelist.value, ...createdList.value]
    : createdList.value;
  async function onSelect(mix: Playlist) {
    show.value = false;
    await addTrack(mix.id, track);
  }
  if (!favToMixVm) {
    favToMixVm = createApp({
      setup() {
        return () =>
          h(Comp, {
            show: show.value,
            list,
            hide,
            onSelect,
          });
      },
    });
    const el = document.createElement("div");
    document.body.appendChild(el);
    favToMixVm.mount(el);
  }
}

搜尋

搜尋頁面中,實現功能的功能有

  • 搜尋歌手、歌單、歌曲、使用者
  • 搜尋提示(函式防抖)
  • 熱門搜尋、歷史搜尋

歌單詳情

在歌單頁面中,可以收藏和取消收藏其他使用者的歌單,點選歌曲可以播放整個歌單歌曲列表,如果是自己的歌單可以將歌曲從歌單中刪除。補充:排行榜的詳情頁以及每日推薦其實也是歌單,只是歌曲樣式稍有不同。

評論

歌曲和歌單的評論是同一個元件,只是傳入的引數不同。
實現的功能有

  • 評論列表
  • 釋出評論
  • 回覆評論
  • 查看回複列表
  • 點贊評論
  • 刪除自己釋出的評論

使用者主頁

關注/粉絲列表

歌手主頁

歌手主頁和使用者主頁使用的是同一個佈局元件,通過傳入 slot 插槽, 呈現不同的主體。

歌手分類

歌單廣場

排行榜

專案執行

  1. 克隆到本地
    git clone https://github.com/bianyingchun/react-music.git

  2. 安裝依賴
    yarn install

  3. 執行
    yarn serve

  4. 打包
    yarn build