網易雲音樂 Node.js API service
可以使用下列任一命令安裝這個新的包:
npm install -g @vue/cli
# OR
yarn global add @vue/cli
安裝之後,你就可以在命令列中存取 命令。你可以通過簡單執行 ,看看是否展示出了一份所有可用命令的幫助資訊,來驗證它是否安裝成功。vuevue
你還可以用這個命令來檢查其版本是否正確:
vue --version
如需升級全域性的 Vue CLI 包,請執行:
npm update -g @vue/cli
# 或者
yarn global upgrade --latest @vue/cli
建立一個新專案:
vue create music163_app
執行專案
npm run serve
所謂的適配佈局,是讓頁面盒子的高度,寬度,內外邊距,邊框大小,文字的大小,定位的元素位置等能夠根據螢幕寬度自動改變大小和位置,從而達到對不同的螢幕都能夠做到最完美的展示,這就是rem適配佈局的優秀地方。
rem.js
function remSize(){
//獲取裝置的寬度
var deviceWidth=document.documentElement.clientWidth || window.innerWidth
if(deviceWidth>=750){
deviceWidth=750
}
if(deviceWidth<=320){
deviceWidth=320
}
//750px-->1rem=100px,375px-->1rem=50px
document.documentElement.style.fontSize=(deviceWidth/7.5)+'px'
// 設定字型大小
document.querySelector('body').style.fontSize=0.3+"rem"
}
remSize()
// 當視窗發生變化就呼叫
window.onresize=function(){
remSize()
}
在主頁面引入rem佈局
index.html
<script src="<%= BASE_URL %>js/rem.js"></script>
第一步:拷貝專案下面生成的symbol程式碼:
index.html
//at.alicdn.com/t/*****.js
第二步:加入通用css程式碼(引入一次就行):
.home.vue(設定全域性樣式)
<style type="text/css">
.icon {
width: 1em; height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>
第三步:挑選相應圖示並獲取類名,應用於頁面:
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-xxx"></use>
</svg>
TopNav.vue
<template>
<div class="topNav">
<div class="topleft">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-31liebiao"></use>
</svg>
</div>
<div class="topContent">
<span @click="$router.push('/infoUser')">我的</span>
<span class="active">發現</span>
<span>雲村</span>
<span>視訊</span>
</div>
<div class="topRight">
<svg class="icon" aria-hidden="true" @click="$router.push('/search')">
<use xlink:href="#icon-sousuo"></use>
</svg>
</div>
</div>
</template>
<style lang="less" scoped>
.topNav{
width: 100%;
height: 1rem;
padding: .2rem;
display: flex;
justify-content: space-between;
align-items: center;
.topContent{
width: 65%;
height: 100%;
display: flex;
justify-content: space-around;
// align-items: center;
font-size: .36rem;
.active{
font-weight: 900;
}
}
}
</style>
# 通過 npm 安裝
npm i [email protected] -D
# 通過 yarn 安裝
yarn add [email protected] -D
# 通過 pnpm 安裝
pnpm add [email protected] -D
import vue from '@vitejs/plugin-vue';
import styleImport, { VantResolve } from 'vite-plugin-style-import';
export default {
plugins: [
vue(),
styleImport({
resolves: [VantResolve()],
}),
],
};
plugins>index.js
import { Swipe, SwipeItem,Button,Popup } from 'vant';
// 放入陣列中
let plugins=[
Swipe,SwipeItem,Button,Popup
]
export default function getVant(app){
plugins.forEach((item)=>{
return app.use(item)
})
}
main.js
import getVant from './plugins'
const app=createApp(App)
getVant(app)
按需使用,外掛式引入
SwpierTop.vue
<template>
<div id="swiperTop">
<van-swipe :autoplay="3000" lazy-render>
<van-swipe-item v-for="image in state.images" :key="image">
<img :src="image.pic" />
</van-swipe-item>
</van-swipe>
</div>
</template>
<script>
import axios from "axios";
import { getBanner } from "@/request/api/home.js";
import { reactive, onMounted } from "vue";
export default {
setup() {
const state = reactive({
images: [
"https://img.yzcdn.cn/vant/apple-1.jpg",
"https://img.yzcdn.cn/vant/apple-2.jpg",
],
});
onMounted(async () => {
// axios.get('http://localhost:3000/banner?type=2').then((res)=>{
// console.log(res);
// state.images=res.data.banners
// console.log(state.images);
// })
let res = await getBanner();
state.images=res.data.banners
console.log(res);
});
return { state };
},
};
</script>
<style lang="less">
#swiperTop {
//需要在上面自己新增一個id
.van-swipe {
width: 100%;
height: 3rem;
.van-swipe-item {
padding: 0 0.2rem;
img {
width: 100%;
height: 100%;
border-radius: 0.2rem;
}
}
.van-swipe__indicator--active {
background-color: rgb(219, 130, 130);
}
}
}
</style>
獲取網易雲介面資料
axios中文檔案
安裝axios
npm install axios
引入axios
import axios from "axios";
執行get請求
setup() {
const state = reactive({
images: [
"https://img.yzcdn.cn/vant/apple-1.jpg",
"https://img.yzcdn.cn/vant/apple-2.jpg",
],
});
onMounted(async () => {
//執行 GET 請求
axios.get('http://localhost:3000/banner?type=2').then((res)=>{
console.log(res);
state.images=res.data.banners
console.log(state.images);
})
頁面渲染
<img :src="image.pic" />
request>index.js
// 建立axios 範例,把域名基礎路徑抽取出來
import axios from 'axios';
let service=axios.create({
baseURL:"http://localhost:3000/",
timeout:3000
})
export default service
request>api>home.js
import service from "..";
// 獲取首頁輪播圖的資料
export function getBanner(){
return service({
method:"GET",
url:"/banner?type=2",
})
}
import { getBanner } from "@/request/api/home.js";
onMounted(async () => {
let res = await getBanner();
state.images=res.data.banners
IconList.vue
<template>
<div class="iconList">
<div class="iconItem">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-tuijian"></use>
</svg>
<span>每日推薦</span>
</div>
<div class="iconItem">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-zhibo"></use>
</svg>
<span>私人FM</span>
</div>
<div class="iconItem">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-gedan"></use>
</svg>
<span>歌單</span>
</div>
<div class="iconItem">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-paihangbang"></use>
</svg>
<span>排行榜</span>
</div>
</div>
</template>
<style lang="less" scoped>
.iconList{
width: 100%;
height: 2rem;
margin-top: .2rem;
display: flex;
justify-content: space-around;
align-items: center;
.iconItem{
width: 25%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
.icon{
width: 1rem;
height: 1rem;
}
}
}
</style>
request>api
//獲取發現好歌單
export function getMusicList(){
return service({
method:"GET",
url:"/personalized?limit=10"
})
}
MusicList.vue
Vue2
data() {
return {
musicList: [],//定義陣列接收資料
};
},
methods: {
async getGnedan() {
let res = await getMusicList();
console.log(res);
this.musicList = res.data.result;
},
},
mounted() {
this.getGnedan();
},
vue3
MusicList.vue
// Vue3
setup() {
const state = reactive({
musicList: [],//定義陣列接收資料,reactive()可以響應式修改資料
});
onMounted(async () => { //onMounted執行資料
let res = await getMusicList();
console.log(res);
state.musicList = res.data.result;
});
return { state,changeCount };//返回資料
},
MusicList.vue
<van-swipe-item v-for="item in state.musicList" :key="item">
<router-link :to="{path:'/itemMusic',query:{id:item.id}}">
<img :src="item.picUrl" alt="" />
<span class="playCount">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-gl-play-copy"></use>
</svg>
{{ changeCount(item.playCount) }}
</span>
<span class="name">{{ item.name }}</span>
</router-link>
</van-swipe-item>
views>Home.vue: 1.import 2.components註冊,div 參照
<div>
<TopNav />
<SwpierTop />
<IconList/>
<MusicList/>
</div>
import MusicList from "@/components/home/MusicList.vue";
components: {
TopNav,
SwpierTop,
IconList,
MusicList,
},
設定路由
router.js
{
path: '/itemMusic',
name: 'ItemMusic',
component: () => import('../views/ItemMusic.vue')
},
使用router-link路由跳轉並query傳參
MusicList.vue
<router-link :to="{path:'/itemMusic',query:{id:item.id}}">
</router-link>
使用useRoute可以獲取路由傳遞的引數
onMounted(() => {
let id = useRoute().query.id;
console.log(id);
}
request>item.js
//獲取歌單詳情頁的資料
export function getMusicItemList(data){
return service({
method:"GET",
url:`/playlist/detail?id=${data}`
})
*呼叫getMusicItemList方法
setup() {
const state = reactive({
playlist: {}, //陣列接收歌單詳情頁的資料
});
onMounted(async () => {
let id = useRoute().query.id;////onMounted執行資料
console.log(id);
// 獲取歌單詳情頁
let res = await getMusicItemList(id);
console.log(res);
state.playlist = res.data.playlist;//將獲取的api資料儲存到陣列playlist中
}
return { state };//返回資料
父元件ItemMusic
<ItemMusicTop :playlist="state.playlist" /> //將父元件的state.playlist傳給子元件ItemMusicTop
components: {
ItemMusicTop,
ItemMusicList,
},
子元件ItemMusicTop
setup(props){
console.log(playlist)
}
props: ["playlist"],//子元件接收playlist資料
ItemMusicTop
<template>
<div class="itemMusicTop">
<img :src="playlist.coverImgUrl" alt="" class="bgimg" />
<div class="itemLeft">
<svg class="icon" aria-hidden="true" @click="$router.go(-1)">
<use xlink:href="#icon-zuojiantou"></use>
</svg>
<span>歌單</span>
</div>
<div class="itemRight">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-sousuo"></use>
</svg>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-31liebiao"></use>
</svg>
</div>
</div>
<div class="itemTopContent">
<div class="contentLeft">
<img :src="playlist.coverImgUrl" alt="" />
<div class="palyCount">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-gl-play-copy"></use>
</svg>
<span>{{ changeCount(playlist.playCount) }}</span>
</div>
</div>
<div class="contentRight">
<p class="rightP_one">{{ playlist.name }}</p>
<div class="right_img">
<img :src="playlist.creator.backgroundUrl" alt="" />
<span>{{ playlist.creator.nickname }}</span>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-youjiantou"></use>
</svg>
</div>
<p class="rightP_two">
<span>{{ playlist.description }}</span>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-youjiantou"></use>
</svg>
</p>
</div>
</div>
<div class="itemTopFooter">
<div class="footerItem">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-iconfontzhizuobiaozhun023110"></use>
</svg>
<span>{{ playlist.commentCount }}</span>
</div>
<div class="footerItem">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-fenxiang"></use>
</svg>
<span>{{ playlist.shareCount }}</span>
</div>
<div class="footerItem">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-iconfontzhizuobiaozhun023146"></use>
</svg>
<span>下載</span>
</div>
<div class="footerItem">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-show_duoxuan"></use>
</svg>
<span>多選</span>
</div>
</div>
</template>
*背景虛化
.bgimg {
width: 100%;
height: 11rem;
position: absolute;
z-index: -1;
filter: blur(30px);
}
@click="$router.go(-1)
原因:在itemmusic中onMoonunted裡資料獲取都是非同步的,在子元件頁面渲染時資料還沒有獲取到。為防止資料丟失,儲存本地資料
父元件itemMuisc的onMoonunted
// 防止頁面重新整理,資料丟失,將資料儲存到sessionStorage中,setItem儲存value,getItem獲取value
sessionStorage.setItem("itemDetail", JSON.stringify(state));// 將state以json的格式儲存到key為itemDetail的對談儲存中
判斷頁面重新整理後資料是否為空,空則呼叫sessionStorage裡的資料
子元件ItemMusicTop的onMounted
// 通過props進行傳值,判斷如果資料拿不到,就獲取sessionStorage中的資料
if(props.playlist.creator=""){
props.playlist.creator = JSON.parse(sessionStorage.getItem().playlist).creator //獲取sessionStorage裡的value有playlist的creator,並將json格式資料轉為物件資料
}
*獲取歌單歌曲詳情資料
//獲取歌單的所有歌曲
export function getItemList(data){
return service({
method:"GET",
url:`/playlist/track/all?id=${data.id}&limit=${data.limit}&offset=${data.offset}`
})
}
itemMusic
const state = reactive({
itemList: [], //1.定義陣列儲存歌單的歌曲資料
});
//2.獲取歌單的歌曲
onMounted(async () => {
let result = await getItemList({ id, limit: 10, offset: 0 });
console.log(result);
state.itemList = result.data.songs
}
return { state }; //3.返回資料
//4.將父元件的state.itemList轉給子元件ItemMusicList的itemList
<ItemMusicList
:itemList="state.itemList"
/>
//props子元件接收資料itemList
setup(props) {
console.log(props);
},
props: ["itemList"],
<div class="item" v-for="(item, i) in itemList" :key="i">
<span class="leftSpan">{{ i + 1 }}</span>
//如果有mv顯示mv圖示
<svg class="icon bofang" aria-hidden="true" v-if='item.mv !=0'>
<use xlink:href="#icon-shipin"></use>
</svg>
<span v-for="(item1, index) in item.ar" :key="index">{{
item1.name
}}</span>
因為底部元件全頁面都存在,所以是全域性元件
app.vue
//1.import 2.components註冊,div 參照
<template>
<FooterMusic v-show="$store.state.isFooterMusic"/>
</template>
<script>
import FooterMusic from "@/components/item/FooterMusic.vue"
export default {
components:{
FooterMusic
}
}
stroe
playList: [{ //播放列表預設
al: {
id: 89039055,
name: "雨愛抖音版",
pic: 109951164966568500,
picUrl: "https://p1.music.126.net/2f6UgY8Jc0Dy6jufMdIZeQ==/109951164966568495.jpg",
pic_str: "109951164966568495"
},
id: 1446137141,
name: "雨愛(抖音版)",
ar:[{name: "灝灝灝仔"}]
}],
playListIndex: 0, //預設下標為0,當切換歌曲時憑下標切換
*獲取vuex裡的playList資料
itemMusicList
//使用vuex的內建函數mapState 獲取vuex資料,解構賦值
import { mapState } from "vuex";
computed: {
...mapState(["playList", "playListIndex", "isbtnShow", "detailShow"]),
},
*頁面渲染
itemMusicList
<img :src="playList[playListIndex].al.picUrl" alt="" />
<div>
<p>{{ playList[playListIndex].name }}</p>
<span>橫滑切換上下首哦</span>
</div>
模板字串(template string)是增強版的字串,用反引號``標識。它可以當作普通字串使用,也可以用來定義多行字串,或者在字串中嵌入變數,變數名寫在$()中。
ref ref 被用來給元素或子元件註冊參照資訊。參照資訊將會註冊在父元件的 $refs 物件上。如果在普通的 DOM 元素上使用,參照指向的就是 DOM 元素;如果用在子元件上,參照就指向元件.
//範例
<div ref="test" @click="test">ref 測試</div>
mounted(){
console.log(this.$refs.test);
},
*播放
<audio
ref="audio"
:src="`https://music.163.com/song/media/outer/url?id=${playList[playListIndex].id}.mp3`"
></audio>//因為src裡的id是變數,所以用模板字串``
*新增播放方法
methods: {
play: function () {
this.$refs.audio.play();
}
}
*使用播放方法
<svg class="icon liebiao" aria-hidden="true" @click="play" >
<use xlink:href="#icon-weibiaoti--"></use>
</svg>
*播放暫停切換
store
state:{
isbtnShow: true, //暫停按鈕的顯示
}
mutations: {
updateIsbtnShow: function (state, value) {
state.isbtnShow = value //呼叫updateIsbtnShow()方法傳value從而改變state.isbtnShow的布林值,
},
*播放暫停切換方法
play: function () {
// 判斷音樂是否播放
if (this.\(refs.audio.paused) {
this.\)refs.audio.play();
this.updateIsbtnShow(false);
} else {
this.$refs.audio.pause();
this.updateIsbtnShow(true);
}
},
*解構賦值和解構方法
//解構賦值
computed: {
...mapState(["playList", "playListIndex", "isbtnShow"]),
},
//解構方法
...mapMutations(["updateIsbtnShow"]),
*頁面播放暫停圖示新增判斷事件實現播放暫停圖示的切換
<svg class="icon liebiao" aria-hidden="true" @click="play" v-if="isbtnShow">
<use xlink:href="#icon-bofanganniu"></use>
</svg>
<svg class="icon liebiao" aria-hidden="true" @click="play" v-else>
<use xlink:href="#icon-weibiaoti--"></use>
</svg>