本文最后更新于89 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com
最近学了学Vue框架,正好找到了网易云民间大佬的API,就尝试复刻了一下,着重说一下路由,axios和pinia部分,以及一些功能的实现。
路由:通过Vue的router组件,用webhistory模式进行配置,并使用query进行传递数据
import { createRouter, createWebHistory } from 'vue-router'
import MusicHall from '../views/MusicHall.vue'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'MusicHall',
component: MusicHall,
},
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue'),
},
{
path: '/mymusic',
name: 'MyMusic',
component: () => import('../views/MyMusic.vue'),
},
{
path: '/player',
name: 'Player',
component: () => import('../views/Player.vue'),
},
{
path: '/search',
name: 'Search',
component: () => import('../views/Search.vue'),
},
{
path: '/music-list',
name: 'MusicList',
component: () => import('../views/MusicList.vue'),
},
],
})
export default router
axios:负责API与前端的连接,封装了对接受与发送的拦截和get与post方法的封装,对请求头添加了保存在了浏览器本地的token,规定了get与post时参数的传递方式
import axios from "axios";
const instance = axios.create({
baseURL: 'http://localhost:3000',
timeout: 10000,
withCredentials: true,
});
instance.interceptors.request.use( (config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
}, function (error) {
return Promise.reject(error);
});
instance.interceptors.response.use( (response) => {
return response.data;
}, function (error) {
return Promise.reject(error);
});
export function get(url,params = {},config = {}){
return instance.get(url,{params,...config})
}
export function post(url, data={}, config={}) {
return instance.post(url, data, config);
}
const api = {
get,
post,
};
export default api;
pinia:在前端pinia里用json格式保存,并在浏览器中以字符串格式保存
import { ref,computed} from 'vue';
import { defineStore } from 'pinia';
const KEY = 'nc_user';
export const useUserStore = defineStore('user', () => {
const user = ref(null);
const username = ref(null);
const isLoggedIn = computed(() => !!user.value);
const setUser=(Payload) => {
const normallized={
id: Payload.id,
nickname: Payload.nickname,
avatar: Payload.avatar,
}
user.value = normallized;
localStorage.setItem(KEY,JSON.stringify(normallized));
}
const clearUser=()=>{
user.value = null;
localStorage.removeItem(KEY);
}
const InitFromLocal=()=>{
const savedUser = localStorage.getItem(KEY);
if(savedUser){
user.value = JSON.parse(savedUser);
}}
InitFromLocal();
return {
user,
isLoggedIn,
setUser,
clearUser,
};
})
音乐播放器:

进度条显示与跳转:对播放器的加载,播放,结束分别调用回调函数,以获取总时长,已播放时长等信息,并计算比例,得出长度,同时定义点击事件,获取点击部位与进度条长度比例,换算为时间比例,进行跳转。
<audio
:src="audioUrl"
v-if="audioUrl"
ref="audioRef"
class="audio-hidden"
@loadedmetadata="handleLoadedMetadata"
@timeupdate="handleTimeUpdate"
@ended="handleAudioEnded"
></audio>
const handleTogglePlay = async () => {
if (!audioUrl.value) return;
if (audioRef.value.paused) {
audioRef.value.play().then(()=>{
isPlaying.value = true
}).catch(() =>{});
} else {
audioRef.value.pause()
isPlaying.value = false
}
};
唱片旋转:通过已播放的时间乘度数取余360得出需要倾斜的角度,然后平滑旋转即可解决。
// 计算旋转角度
const calculateRotation = () => {
if(isPlaying.value) {
// 根据播放的时间来计算旋转角度
// 假设按baseSpeed的速度持续旋转
const elapsed = currentTime.value; // 从歌曲开始播放到现在的时间
rotationAngle.value = (elapsed * baseSpeed.value) % 360;
}
};