日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

调用网易云Api接口实现移动Web网易云部分功能(搜索+列表+播放)

發布時間:2023/12/10 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 调用网易云Api接口实现移动Web网易云部分功能(搜索+列表+播放) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

知識點自測

  • 知道reset.css和flexible.js的作用
  • 什么是組件庫-例如bootstrap作用
  • yarn命令的使用
  • 組件名字用name屬性方式注冊
  • 如何自定義組件庫樣式

鋪墊(自學)

本地接口項目部署

下載網易云音樂node接口項目, 在本地啟動, 為我們vue項目提供數據支持

項目地址

備用地址

下載后, 安裝所有依賴, 在本地啟動起來, 測試訪問此地址是否有數據

http://localhost:3000, 看到如下頁面就成功了

> 總結: 前端請求本地的node項目, node服務器偽裝請求去拿網易云音樂服務器數據轉發回給自己前端

學習目標

  • 能夠掌握vant組件庫的使用
  • 能夠掌握vant組件自定義樣式能力
  • 能夠掌握組件庫使用和文檔使用能力
  • 能夠完成網易云音樂案例
  • 1.1 網易云音樂-本地接口啟動

    目的: 啟動本地網易云音樂API服務

    1.2 網易云音樂-前端項目初始化

    目標: 初始化項目, 下載必備包, 引入初始文件, 配置按需自動引入vant, 創建頁面組件

  • 初始化工程

    vue create music-demo
  • 下載需要的所有第三方依賴包

    yarn add axios vant vue-router
  • 引入筆記代碼里準備好的reset.css和flexible.js - 實現樣式初始化和適配問題 - 引入到main.js

  • 本次vant使用自動按需引入的方式

    文檔: https://vant-contrib.gitee.io/vant/#/zh-CN/quickstart

    yarn add babel-plugin-import -D

    在babel.config.js - 添加插件配置

    plugins: [['import', {libraryName: 'vant',libraryDirectory: 'es',style: true}, 'vant'] ]
  • 1.3 網易云音樂-需求分析

    根據需求, 創建路由所需要的5個頁面的組件

    Layout(布局, 頂部導航和底部導航) > 二級路由 Home 和 Search以及播放Play


    創建需要的views下的頁面組件4個

    views/Layout/index.vue - 負責布局(上下導航 - 中間二級路由切換首頁和搜索頁面)

    /* 中間內容區域 - 容器樣式(留好上下導航所占位置) */ .main {padding-top: 46px;padding-bottom: 50px; }

    views/Home/index.vue - 標題和歌名樣式

    /* 標題 */ .title {padding: 0.266667rem 0.24rem;margin: 0 0 0.24rem 0;background-color: #eee;color: #333;font-size: 15px; } /* 推薦歌單 - 歌名 */ .song_name {font-size: 0.346667rem;padding: 0 0.08rem;margin-bottom: 0.266667rem;word-break: break-all;text-overflow: ellipsis;display: -webkit-box; /** 對象作為伸縮盒子模型顯示 **/-webkit-box-orient: vertical; /** 設置或檢索伸縮盒對象的子元素的排列方式 **/-webkit-line-clamp: 2; /** 顯示的行數 **/overflow: hidden; /** 隱藏超出的內容 **/ }

    views/Search/index.vue

    /* 搜索容器的樣式 */ .search_wrap {padding: 0.266667rem; }/*熱門搜索文字標題樣式 */ .hot_title {font-size: 0.32rem;color: #666; }/* 熱搜詞_容器 */ .hot_name_wrap {margin: 0.266667rem 0; }/* 熱搜詞_樣式 */ .hot_item {display: inline-blockheight: 0.853333rem;margin-right: 0.213333rem;margin-bottom: 0.213333rem;padding: 0 0.373333rem;font-size: 0.373333rem;line-height: 0.853333rem;color: #333;border-color: #d3d4da;border-radius: 0.853333rem;border: 1px solid #d3d4da; }

    Play頁面的index(可以全文復制)

    <template><div class="play"><!-- 模糊背景(靠樣式設置), 固定定位 --><divclass="song-bg":style="`background-image: url(${songInfo && songInfo.al && songInfo.al.picUrl}?imageView&thumbnail=360y360&quality=75&tostatic=0);`"></div><!-- 播放頁頭部導航 --><div class="header"><van-iconname="arrow-left"size="20"class="left-incon"@click="$router.back()"/></div><!-- 留聲機 - 容器 --><div class="song-wrapper"><!-- 留聲機本身(靠css動畫做旋轉) --><divclass="song-turn ani":style="`animation-play-state:${playState ? 'running' : 'paused'}`"><div class="song-img"><!-- &&寫法是為了防止報錯, 有字段再繼續往下訪問屬性 --><imgstyle="width: 100%":src="`${songInfo && songInfo.al && songInfo.al.picUrl}?imageView&thumbnail=360y360&quality=75&tostatic=0`"alt=""/></div></div><!-- 播放按鈕 --><div class="start-box" @click="audioStart"><span class="song-start" v-show="!playState"></span></div><!-- 播放歌詞容器 --><div class="song-msg"><!-- 歌曲名 --><h2 class="m-song-h2"><span class="m-song-sname">{{ songInfo.name }}-{{songInfo && songInfo.ar && songInfo.ar[0].name}}</span></h2><!-- 歌詞部分-隨著時間切換展示一句歌詞 --><div class="lrcContent"><p class="lrc">{{ curLyric }}</p></div></div><!-- 留聲機 - 唱臂 --><div class="needle" :style="`transform: rotate(${needleDeg});`"></div></div><!-- 播放音樂真正的標簽看接口文檔: 音樂地址需要帶id去獲取(但是有的歌曲可能404)https://binaryify.github.io/NeteaseCloudMusicApi/#/?id=%e8%8e%b7%e5%8f%96%e9%9f%b3%e4%b9%90-url--><audioref="audio"preload="true":src="`https://music.163.com/song/media/outer/url?id=${id}.mp3`"></audio></div> </template><script> // 獲取歌曲詳情和 歌曲的歌詞接口 import { getSongByIdAPI, getLyricByIdAPI } from "@/api"; import { Icon } from "vant"; export default {components: {[Icon.name]: Icon,},name: "play",data() {return {playState: false, // 音樂播放狀態(true暫停, false播放)id: this.$route.query.id, // 上一頁傳過來的音樂idsongInfo: {}, // 歌曲信息lyric: {}, // 歌詞枚舉對象(需要在js拿到歌詞寫代碼處理后, 按照格式保存到這個對象)curLyric: "", // 當前顯示哪句歌詞lastLy: "", // 記錄當前播放歌詞};},computed: {needleDeg() {// 留聲機-唱臂的位置屬性return this.playState ? "-7deg" : "-38deg";},},methods: {async getSong() {// 獲取歌曲詳情, 和歌詞方法const res = await getSongByIdAPI(this.id);this.songInfo = res.data.songs[0];// 獲取-并調用_formatLyr方法, 處理歌詞const lyrContent = await getLyricByIdAPI(this.id);const lyricStr = lyrContent.data.lrc.lyric;this.lyric = this._formatLyr(lyricStr);// 初始化完畢先顯示零秒歌詞this.curLyric = this.lyric[0];},_formatLyr(lyricStr) {// 可以看network觀察歌詞數據是一個大字符串, 進行拆分.let reg = /\[.+?\]/g; //let timeArr = lyricStr.match(reg); // 匹配所有[]字符串以及里面的一切內容, 返回數組console.log(timeArr); // ["[00:00.000]", "[00:01.000]", ......]let contentArr = lyricStr.split(/\[.+?\]/).slice(1); // 按照[]拆分歌詞字符串, 返回一個數組(下標為0位置元素不要,后面的留下所以截取)console.log(contentArr);let lyricObj = {}; // 保存歌詞的對象, key是秒, value是顯示的歌詞timeArr.forEach((item, index) => {// 拆分[00:00.000]這個格式字符串, 把分鐘數字取出, 轉換成秒let ms = item.split(":")[0].split("")[2] * 60;// 拆分[00:00.000]這個格式字符串, 把十位的秒拿出來, 如果是0, 去拿下一位數字, 否則直接用2位的值let ss =item.split(":")[1].split(".")[0].split("")[0] === "0"? item.split(":")[1].split(".")[0].split("")[1]: item.split(":")[1].split(".")[0];// 秒數作為key, 對應歌詞作為valuelyricObj[ms + Number(ss)] = contentArr[index];});// 返回得到的歌詞對象(可以打印看看)console.log(lyricObj);return lyricObj;},audioStart() {// 播放按鈕 - 點擊事件if (!this.playState) {// 如果狀態為falsethis.$refs.audio.play(); // 調用audio標簽的內置方法play可以繼續播放聲音} else {this.$refs.audio.pause(); // 暫停audio的播放}this.playState = !this.playState; // 點擊設置對立狀態},showLyric() {// 監聽播放audio進度, 切換歌詞顯示this.$refs.audio.addEventListener("timeupdate", () => {let curTime = Math.floor(this.$refs.audio.currentTime);// 避免空白出現if (this.lyric[curTime]) {this.curLyric = this.lyric[curTime];this.lastLy = this.curLyric;} else {this.curLyric = this.lastLy;}});},},mounted() {this.getSong();this.showLyric();console.log(this.$route.query.id);}, }; </script><style scoped> .header {height: 50px; } .play {position: fixed;top: 0;left: 0;right: 0;bottom: 0;z-index: 1000; } .song-bg {background-color: #161824;background-position: 50%;background-repeat: no-repeat;background-size: auto 100%;transform: scale(1.5);transform-origin: center;position: fixed;left: 0;right: 0;top: 0;height: 100%;overflow: hidden;z-index: 1;opacity: 1;filter: blur(25px); /*模糊背景 */ } .song-bg::before {/*純白色的圖片做背景, 歌詞白色看不到了, 在背景前加入一個黑色半透明蒙層解決 */content: " ";background: rgba(0, 0, 0, 0.5);position: absolute;left: 0;top: 0;right: 0;bottom: 0; } .song-wrapper {position: fixed;width: 247px;height: 247px;left: 50%;top: 50px;transform: translateX(-50%);z-index: 10001; } .song-turn {width: 100%;height: 100%;background: url("./img/bg.png") no-repeat;background-size: 100%; } .start-box {position: absolute;width: 156px;height: 156px;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);display: flex;justify-content: center;align-items: center; } .song-start {width: 56px;height: 56px;background: url("./img/start.png");background-size: 100%; } .needle {position: absolute;transform-origin: left top;background: url("./img/needle-ab.png") no-repeat;background-size: contain;width: 73px;height: 118px;top: -40px;left: 112px;transition: all 0.6s; } .song-img {width: 154px;height: 154px;position: absolute;left: 50%;top: 50%;overflow: hidden;border-radius: 50%;transform: translate(-50%, -50%); } .m-song-h2 {margin-top: 20px;text-align: center;font-size: 18px;color: #fefefe;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; } .lrcContent {margin-top: 50px; } .lrc {font-size: 14px;color: #fff;text-align: center; } .left-incon {position: absolute;top: 10px;left: 10px;font-size: 24px;z-index: 10001;color: #fff; } .ani {animation: turn 5s linear infinite; } @keyframes turn {0% {-webkit-transform: rotate(0deg);}100% {-webkit-transform: rotate(360deg);} } </style>

    1.4 網易云音樂-路由準備

    目標: 準備路由配置, 顯示不同路由頁面

    router/index.js - 準備路由 - 以及默認顯示Layout, 然后Layout默認顯示二級路由的首頁

    // 路由-相關模塊 import Vue from 'vue' import VueRouter from 'vue-router' import Layout from '@/views/Layout' import Home from '@/views/Home' import Search from '@/views/Search' import Play from '@/views/Play'Vue.use(VueRouter) const routes = [{path: '/',redirect: '/layout'},{path: '/layout',component: Layout,redirect: '/layout/home',children: [{path: 'home',component: Home,meta: { // meta保存路由對象額外信息的title: "首頁"}},{path: 'search',component: Search,meta: {title: "搜索"}}]},{path: '/play',component: Play} ]const router = new VueRouter({routes })export default router

    main.js - 引入路由對象, 注冊到new Vue中

    import router from '@/router'new Vue({render: h => h(App),router }).$mount('#app')

    App.vue中留好router-view顯示路由頁面

    <template><div><router-view></router-view></div> </template>

    總結: 把項目的框搭好, 逐個攻破

    1.5 網易云音樂-Tabbar組件

    目標: 點擊底部導航, 切換路由頁面顯示

    文檔: https://vant-contrib.gitee.io/vant/#/zh-CN/tabbar

  • 注冊Tabbar組件, 在main.js中

    import { Tabbar, TabbarItem } from 'vant'; Vue.use(Tabbar); Vue.use(TabbarItem);
  • 在Layout.vue中使用

    <template><div><div class="main"><!-- 二級路由-掛載點 --><router-view></router-view></div><van-tabbar route><van-tabbar-item replace to="/layout/home" icon="home-o">首頁</van-tabbar-item><van-tabbar-item replace to="/layout/search" icon="search">搜索</van-tabbar-item></van-tabbar></div> </template><script> export default { } </script><style scoped> /* 中間內容區域 - 容器樣式(留好上下導航所占位置) */ .main {padding-top: 46px;padding-bottom: 50px; } </style>
  • 開啟路由模式 route屬性, 和to屬性指向要切換的路由路徑

    <van-tabbar route><van-tabbar-item icon="home-o" to="/layout/home">首頁</van-tabbar-item><van-tabbar-item icon="search" to="/layout/search">搜索</van-tabbar-item> </van-tabbar>
  • 總結: van-tabbar開啟route

    1.6 網易云音樂-NavBar導航組件

    目標: 實現頂部標題展示

    文檔: https://vant-contrib.gitee.io/vant/#/zh-CN/nav-bar

  • main.js - 注冊NavBar組件

    import { NavBar } from 'vant'; Vue.use(NavBar);
  • 復制文檔里的, 然后刪刪只留標題

    <van-nav-bar :title="activeTitle" fixed /><script>export default {activeTitle: "首頁"} </script>
  • 1.7 網易云音樂-NavBar標題切換

    目標: 實現點擊底部導航/刷新非第一頁面頁面, 導航標題正確顯示

    • 在router/index.js - 給$route里需要導航標題的添加meta元信息屬性

      {path: '/layout',component: Layout,redirect: '/layout/home',children: [{path: 'home',component: Home,meta: { // meta保存路由對象額外信息的title: "首頁"}},{path: 'search',component: Search,meta: {title: "搜索"}}]},

      Layout.vue中監聽$route改變:

      給導航active的值設置$route里的元信息的標題

      export default {data() {return {activeTitle: this.$route.meta.title, // "默認"頂部導航要顯示的標題 (默認獲取當前路由對象里的meta中title值)};},// 路由切換 - 偵聽$route對象改變watch: {$route() {this.activeTitle = this.$route.meta.title; // 提取切換后路由信息對象里的title顯示},}, };

    總結: 點擊底部導航和刷新當前網頁, 都能保證導航標題的正確顯示

    1.8 網易云音樂-網絡請求封裝

    目標: 不想把網絡請求散落在各個邏輯頁面里, 不然以后找起來改起來很麻煩

  • 封裝utils/request.js - 基于axios進行二次封裝 - 設置基礎地址

    // 網絡請求 - 二次封裝 import axios from 'axios' axios.defaults.baseURL = "http://localhost:3000" export default axios
  • 封裝src/api/Home.js

    統一封裝網絡請求方法

    // 文件名-盡量和模塊頁面文件名統一(方便查找) import request from '@/utils/request'// 首頁 - 推薦歌單 export const recommendMusic = params => request({url: '/personalized',params// 將來外面可能傳入params的值 {limit: 20} })
  • 在src/api/index.js - 統一導出接口供外部使用

    // api文件夾下 各個請求模塊js, 都統一來到index.js再向外導出 import {recommendMusic} from './Home'export const recommendMusicAPI = recommendMusic // 請求推薦歌單的方法導出
  • 在main.js - 測試使用一下.

    import { recommendMusicAPI } from '@/api/index' async function myFn(){const res = await recommendMusicAPI({limit: 6});console.log(res); } myFn();
  • 總結: 封裝網絡請求方法目的, 方便我們統一管理

    1.9 網易云音樂-首頁-推薦歌單

    接口地址: /personalized

  • 布局采用van-row和van-col

    布局文檔https://vant-contrib.gitee.io/vant/#/zh-CN/col

  • 使用vant內置的圖片組件來顯示圖片

  • 在main.js注冊使用的組件

    import { Col, Row, Image as VanImage } from 'vant';Vue.use(Col); Vue.use(Row); Vue.use(VanImage);
  • 在api/index.js下定義推薦歌單的接口方法

    // 首頁 - 推薦歌單 export const recommendMusic = params => request({url: '/personalized',params// 將來外面可能傳入params的值 {limit: 20} })
  • 把數據請求回來, 用van-image和p標簽展示推薦歌單和歌單名字

    <template><div><p class="title">推薦歌單</p><van-row gutter="6"><van-col span="8" v-for="obj in reList" :key="obj.id"><van-image width="100%" height="3rem" fit="cover" :src="obj.picUrl" /><p class="song_name">{{ obj.name }}</p></van-col></van-row></div> </template><script> import { recommendMusicAPI } from "@/api"; export default {data() {return {reList: [], // 推薦歌單數據};},async created() {const res = await recommendMusicAPI({limit: 6,});console.log(res);this.reList = res.data.result;}, }; </script>
  • 1.10 網易云音樂- 首頁-最新音樂

    目標: van-cell單元格使用

    請求地址: /personalized/newsong

  • 引入van-cell使用 - 注冊組件main.js中

    import {Cell} from 'vant'; Vue.use(Cell);
  • 定義接口請求方法 - api/index.js

    // 首頁 - 推薦最新音樂 export const newMusic = params => request({url: "/personalized/newsong",params })
  • 列表數據鋪設 - 插入自定義標簽

    <template><div><p class="title">推薦歌單</p><div><van-row gutter="5"><van-col span="8" v-for="obj in recommendList" :key="obj.id"><van-image fit="cover" :src="obj.picUrl" /><p class="song_name">{{ obj.name }}</p></van-col></van-row></div><p class="title">最新音樂</p><van-cell center v-for="obj in songList" :key="obj.id" :title="obj.name" :label="obj.song.artists[0].name + ' - ' + obj.name"><template #right-icon><van-icon name="play-circle-o" size="0.6rem"/></template></van-cell></div> </template><script> import { recommendMusicAPI, newMusicAPI } from "@/api"; export default {data() {return {reList: [], // 推薦歌單數據songList: [], // 最新音樂數據};},async created() {const res = await recommendMusicAPI({limit: 6,});console.log(res);this.reList = res.data.result;const res2 = await newMusicAPI({limit: 20})console.log(res2);this.songList = res2.data.result}, }; </script>
  • 1.11 網易云音樂-搜索-熱搜關鍵字

    目標: 完成熱搜關鍵字鋪設

    搜索框 – van-search組件

    api/Search.js – 熱搜關鍵字 - 接口方法

    Search/index.vue引入-獲取熱搜關鍵字 - 鋪設頁面

    點擊文字填充到輸入框

  • 準備搜索界面標簽
  • <template><div><van-searchshape="round"placeholder="請輸入搜索關鍵詞"/><!-- 搜索下容器 --><div class="search_wrap"><!-- 標題 --><p class="hot_title">熱門搜索</p><!-- 熱搜關鍵詞容器 --><div class="hot_name_wrap"><!-- 每個搜索關鍵詞 --><spanclass="hot_item">熱搜關鍵字</span></div></div></div> </template> <script> export default {} </script><style scoped> /* 搜索容器的樣式 */ .search_wrap {padding: 0.266667rem; }/*熱門搜索文字標題樣式 */ .hot_title {font-size: 0.32rem;color: #666; }/* 熱搜詞_容器 */ .hot_name_wrap {margin: 0.266667rem 0; }/* 熱搜詞_樣式 */ .hot_item {display: inline-block;height: 0.853333rem;margin-right: 0.213333rem;margin-bottom: 0.213333rem;padding: 0 0.373333rem;font-size: 0.373333rem;line-height: 0.853333rem;color: #333;border-color: #d3d4da;border-radius: 0.853333rem;border: 1px solid #d3d4da; }/* 給單元格設置底部邊框 */ .van-cell {border-bottom: 1px solid lightgray; } </style>
  • api/Search.js - 定義熱門搜索接口方法和搜索結果方法
  • import request from '@/utils/request'// 熱搜關鍵字 export const hotSearch = () => request({url: '/search/hot' })// 搜索結果列表 export const searchResult = params => request({url: '/cloudsearch',params })
  • api/index.js - 導入使用并統一導出
  • // 統一出口 // 你也可以在邏輯頁面里.vue中直接引入@/api/Home下的網絡請求工具方法 // 為什么: 我們可以把api所有的方法都統一到一處. import {recommendMusic, hotMusic} from '@/api/Home' import {hotSearch, searchResult} from '@/api/Search'export const recommendMusicAPI = recommendMusic // 把網絡請求方法拿過來 導出 export const hotMusicAPI = hotMusic // 把獲取最新音樂的, 網絡請求方法導出export const hotSearchAPI = hotSearch // 熱搜 export const searchResultAPI = searchResult // 搜索結果
  • created中請求接口-拿到熱搜關鍵詞列表
  • <!-- 每個搜索關鍵詞 --> <spanclass="hot_item"v-for="(obj, index) in hotArr":key="index">{{ obj.first }}</span><script>// 目標: 鋪設熱搜關鍵字// 1. 搜索框van-search組件, 關鍵詞標簽和樣式// 2. 找接口, api/Search.js里定義獲取搜索關鍵詞的請求方法// 3. 引入到當前頁面, 調用接口拿到數據循環鋪設頁面// 4. 點擊關鍵詞把值賦予給van-search的v-model變量import { hotSearchAPI } from "@/api";export default {data(){return {hotArr: [], // 熱搜關鍵字}},async created() {const res = await hotSearchAPI();console.log(res);this.hotArr = res.data.result.hots;},} </script>
  • 點擊熱詞填充到輸入框
  • <van-searchshape="round"v-model="value"placeholder="請輸入搜索關鍵詞"/> <!-- 每個搜索關鍵詞 --> <spanclass="hot_item"v-for="(obj, index) in hotArr":key="index"@click="fn(obj.first)">{{ obj.first }}</span> </div><script>export default {data(){return {value: "",hotArr: [], // 熱搜關鍵字}},// ...省略了createdmethods: {async fn(val) {// 點擊熱搜關鍵詞this.value = val; // 選中的關鍵詞顯示到搜索框},}} </script>

    總結: 寫好標簽和樣式, 拿到數據循環鋪設, 點擊關鍵詞填入到van-search中

    1.12 網易云音樂-搜索-點擊熱詞-搜索結果

    目標: 點擊熱詞填充到輸入框-出搜索結果

    api/Search.js - 搜索結果, 接口方法

    Search/index.vue引入-獲取搜索結果 - 鋪設頁面

    和熱搜關鍵字容器 – 互斥顯示

    點擊文字填充到輸入框, 請求搜索結果鋪設

  • 搜索結果顯示區域標簽+樣式(直接復制/vant文檔找)
  • <!-- 搜索結果 --><div class="search_wrap"><!-- 標題 --><p class="hot_title">最佳匹配</p><van-cellcentertitle='結果名字'><template #right-icon><van-icon name="play-circle-o" size="0.6rem"/></template></van-cell></div>
  • 點擊 - 獲取搜索結果 - 循環鋪設頁面
  • <template><div><van-search shape="round" v-model="value" placeholder="請輸入搜索關鍵詞" /><!-- 搜索下容器 --><div class="search_wrap"><!-- 標題 --><p class="hot_title">熱門搜索</p><!-- 熱搜關鍵詞容器 --><div class="hot_name_wrap"><!-- 每個搜索關鍵詞 --><spanclass="hot_item"v-for="(obj, index) in hotArr":key="index"@click="fn(obj.first)">{{ obj.first }}</span></div></div><!-- 搜索結果 --><div class="search_wrap"><!-- 標題 --><p class="hot_title">最佳匹配</p><van-cellcenterv-for="obj in resultList":key="obj.id":title="obj.name":label="obj.ar[0].name + ' - ' + obj.name"><template #right-icon><van-icon name="play-circle-o" size="0.6rem"/></template></van-cell></div></div> </template> <script> // 目標: 鋪設熱搜關鍵字 // 1. 搜索框van-search組件, 關鍵詞標簽和樣式 // 2. 找接口, api/Search.js里定義獲取搜索關鍵詞的請求方法 // 3. 引入到當前頁面, 調用接口拿到數據循環鋪設頁面 // 4. 點擊關鍵詞把值賦予給van-search的v-model變量// 目標: 鋪設搜索結果 // 1. 找到搜索結果的接口 - api/Search.js定義請求方法 // 2. 再定義methods里getListFn方法(獲取數據) // 3. 在點擊事件方法里調用getListFn方法拿到搜索結果數據 // 4. 鋪設頁面(首頁van-cell標簽復制過來) // 5. 把數據保存到data后, 循環van-cell使用即可(切換歌手字段) // 6. 互斥顯示搜索結果和熱搜關鍵詞 import { hotSearchAPI, searchResultListAPI } from "@/api"; export default {data() {return {value: "",hotArr: [], // 熱搜關鍵字resultList: [], // 搜索結果};},async created() {const res = await hotSearchAPI();console.log(res);this.hotArr = res.data.result.hots;},methods: {async getListFn() {return await searchResultListAPI({keywords: this.value,limit: 20,}); // 把搜索結果return出去// (難點):// async修飾的函數 -> 默認返回一個全新Promise對象// 這個Promise對象的結果就是async函數內return的值// 拿到getListFn的返回值用await提取結果},async fn(val) {// 點擊熱搜關鍵詞this.value = val; // 選中的關鍵詞顯示到搜索框const res = await this.getListFn();console.log(res);this.resultList = res.data.result.songs;},}, }; </script>
  • 互斥顯示, 熱搜關鍵詞和搜索結果列表
  • 總結: 點擊熱詞后, 調用接口傳入關鍵詞, 返回數據鋪設

    1.13 網易云音樂-輸入框-搜索結果

    目標: 監測輸入框改變-拿到搜索結果

    觀察van-search組件是否支持和實現input事件

    綁定@input事件和方法

    在事件處理方法中獲取對應的值使用

    如果搜索不存在的數據-要注意接口返回字段不同

  • 綁定@input事件在van-search上
  • <van-search shape="round" v-model="value" placeholder="請輸入搜索關鍵詞" @input="inputFn"/>
  • 實現輸入框改變 - 獲取搜索結果鋪設
  • async inputFn() {// 輸入框值改變if (this.value.length === 0) {// 搜索關鍵詞如果沒有, 就把搜索結果清空阻止網絡請求發送(提前return)this.resultList = [];return;}const res = await this.getListFn();console.log(res);// 如果搜索結果響應數據沒有songs字段-無數據if (res.data.result.songs === undefined) {this.resultList = [];return;}this.resultList = res.data.result.songs; },

    總結: 監測輸入框改變-保存新的關鍵詞去請求結果回來鋪設

    1.14 網易云音樂-搜索結果-加載更多

    目標: 觸底后, 加載下一頁數據

    觀察接口文檔: 發現需要傳入offset和分頁公式

    van-list組件監測觸底執行onload事件

    配合后臺接口, 傳遞下一頁的標識

    拿到下一頁數據后追加到當前數組末尾即可

  • 設置van-list組件實現相應的屬性和方法, 讓page++去請求下頁數據
  • <van-listv-model="loading":finished="finished"finished-text="沒有更多了"@load="onLoad"><van-cellcenterv-for="obj in resultList":key="obj.id":title="obj.name":label="obj.ar[0].name + ' - ' + obj.name"><template #right-icon><van-icon name="play-circle-o" size="0.6rem" /></template></van-cell></van-list> <script> // 目標: 加載更多 // 1. 集成list組件-定義相關的變量(搞懂變量的作用) -監測觸底事件 // 2. 一旦觸底, 自動執行onload方法 // 3. 對page++, 給后臺傳遞offset偏移量參數-請求下一頁的數據 // 4. 把當前數據和下一頁新來的數據拼接起來用在當前 頁面的數組里 // (切記) - 加載更多數據后,一定要把loading改成false, 保證下一次還能觸發onload方法 export default {data() {return {value: "",hotArr: [], // 熱搜關鍵字resultList: [], // 搜索結果loading: false, // 加載中 (狀態) - 只有為false, 才能觸底后自動觸發onload方法finished: false, // 未加載全部 (如果設置為true, 底部就不會再次執行onload, 代表全部加載完成)page: 1, // 當前搜索結果的頁碼};},// ...省略其他methods: {async getListFn() {return await searchResultListAPI({keywords: this.value,limit: 20,offset: (this.page - 1) * 20, // 固定公式}); // 把搜索結果return出去// (難點):// async修飾的函數 -> 默認返回一個全新Promise對象// 這個Promise對象的結果就是async函數內return的值// 拿到getListFn的返回值用await提取結果},async onLoad() {// 觸底事件(要加載下一頁的數據咯), 內部會自動把loading改為truethis.page++;const res = await this.getListFn();this.resultList = [...this.resultList, ...res.data.result.songs];this.loading = false; // 數據加載完畢-保證下一次還能觸發onload},}, }; </script>

    總結: list組件負責UI層監測觸底, 執行onload函數, page++, 請求下頁數據, 和現在數據合并顯示更多, 設置loading為false, 確保下次觸底還能執行onLoad

    1.15 網易云音樂-加載更多-bug修復

    目標: 如果只有一頁數據/無數據判斷

    無數據/只有一頁數據, finished為true

    防止list組件觸底再加載更多

    還要測試-按鈕點擊/輸入框有數據情況的加載更多

    正確代碼

    async fn(val) {// 點擊熱搜關鍵詞 + this.finished = false; // 點擊新關鍵詞-可能有新的數據this.value = val; // 選中的關鍵詞顯示到搜索框const res = await this.getListFn();console.log(res);this.resultList = res.data.result.songs; + this.loading = false; // 本次數據加載完畢-才能讓list加載更多},async inputFn() { + this.finished = false // 輸入框關鍵字改變-可能有新數據(不一定加載完成了)// 輸入框值改變if (this.value.length === 0) {// 搜索關鍵詞如果沒有, 就把搜索結果清空阻止網絡請求發送(提前return)this.resultList = [];return;}const res = await this.getListFn();console.log(res);+ // 如果搜索結果響應數據沒有songs字段-無數據 + if (res.data.result.songs === undefined) { + this.resultList = []; + return; + }this.resultList = res.data.result.songs; + this.loading = false;},async onLoad() {// 觸底事件(要加載下一頁的數據咯), 內部會自動把loading改為truethis.page++;const res = await this.getListFn(); + if (res.data.result.songs === undefined) { // 沒有更多數據了 + this.finished = true; // 全部加載完成(list不會在觸發onload方法) + this.loading = false; // 本次加載完成 + return; + }this.resultList = [...this.resultList, ...res.data.result.songs]; + this.loading = false; // 數據加載完畢-保證下一次還能觸發onload},

    總結: 在3個函數 上和下, 設置finished還未完成, 最后要把loading改成false, 判斷songs字段, 對這里的值要非常熟悉才可以

    1.16 網易云音樂-輸入框-防抖

    目標: 輸入框觸發頻率過高

    輸入框輸入"asdfghjkl"

    ? 接著快速的刪除

    ? 每次改變-馬上發送網絡請求

    ? 網絡請求異步耗時 – 數據回來后還是鋪設到頁面上

    解決:

    ? 引入防抖功能

    async inputFn() {// 目標: 輸入框改變-邏輯代碼-慢點執行// 解決: 防抖// 概念: 計時n秒, 最后執行一次, 如果再次觸發, 重新計時// 效果: 用戶在n秒內不觸發這個事件了, 才會開始執行邏輯代碼if (this.timer) clearTimeout(this.timer);this.timer = setTimeout(async () => {this.finished = false; // 輸入框關鍵字改變-可能有新數據(不一定加載完成了)// 輸入框值改變if (this.value.length === 0) {// 搜索關鍵詞如果沒有, 就把搜索結果清空阻止網絡請求發送(提前return)this.resultList = [];return;}const res = await this.getListFn();console.log(res);// 如果搜索結果響應數據沒有songs字段-無數據if (res.data.result.songs === undefined) {this.resultList = [];return;}this.resultList = res.data.result.songs;this.loading = false;}, 900); },

    總結: 降低函數執行頻率

    1.17 網易云音樂-頁碼bug修復

    目標: 第一個關鍵詞page已經+到了10, 再第二個關鍵詞應該從1開始

    加載更多時, page已經往后計數了

    重新獲取時, page不是從第一頁獲取的

    點擊搜索/輸入框搜索時, 把page改回1

    代碼如下:

    async fn(val) {// 點擊熱搜關鍵詞 + this.page = 1; // 點擊重新獲取第一頁數據this.finished = false; // 點擊新關鍵詞-可能有新的數據this.value = val; // 選中的關鍵詞顯示到搜索框const res = await this.getListFn();console.log(res);this.resultList = res.data.result.songs;this.loading = false; // 本次數據加載完畢-才能讓list加載更多},async inputFn() {// 目標: 輸入框改變-邏輯代碼-慢點執行// 解決: 防抖// 概念: 計時n秒, 最后執行一次, 如果再次觸發, 重新計時// 效果: 用戶在n秒內不觸發這個事件了, 才會開始執行邏輯代碼if (this.timer) clearTimeout(this.timer);this.timer = setTimeout(async () => { + this.page = 1; // 點擊重新獲取第一頁數據this.finished = false; // 輸入框關鍵字改變-可能有新數據(不一定加載完成了)// 輸入框值改變if (this.value.length === 0) {// 搜索關鍵詞如果沒有, 就把搜索結果清空阻止網絡請求發送(提前return)this.resultList = [];return;}const res = await this.getListFn();console.log(res);// 如果搜索結果響應數據沒有songs字段-無數據if (res.data.result.songs === undefined) {this.resultList = [];return;}this.resultList = res.data.result.songs;this.loading = false;}, 900);},

    總結: 切換時, 讓page頁面回到1

    1.18 網易云音樂-Layout邊距優化

    目標: 上下導航會蓋住中間內容

    我們的頭部導航和底部導航擋住了中間內容

    給中間路由頁面設置上下內邊距即可

    在Layout/index.vue中

    /* 中間內容區域 - 容器樣式(留好上下導航所占位置) */ .main {padding-top: 46px;padding-bottom: 50px; }

    1.19 網易云音樂-SongItem封裝

    目標: 把首頁和搜索結果的歌曲cell封裝起來

    創建src/components/SongItem.vue

    <template><van-cell center :title="name" :label="author + ' - ' + name"><template #right-icon><van-icon name="play-circle-o" size="0.6rem"/></template></van-cell> </template><script> export default {props: {name: String, // 歌名author: String, // 歌手id: Number, // 歌曲id (標記這首歌曲-為將來跳轉播放頁做準備)} }; </script><style scoped> /* 給單元格設置底部邊框 */ .van-cell {border-bottom: 1px solid lightgray; } </style>

    Home/index.vue - 重構

    注意: author字段不同

    <SongItem v-for="obj in songList":key="obj.id":name="obj.name":author="obj.song.artists[0].name":id="obj.id" ></SongItem>

    Search/index.vue - 重構

    注意: author字段不同

    <SongItemv-for="obj in resultList":key="obj.id":name="obj.name":author="obj.ar[0].name":id="obj.id" ></SongItem>

    總結: 遇到重復標簽要封裝

    1.20 網易云音樂-播放音樂

    目標: 從預習資料拿到播放的api和頁面, 配置好路由規則

    時間關系,這個頁面不用寫, 直接用, 注釋在備課代碼里寫好了

    組件SongItem里 – 點擊事件

    api/Play.js – 提前準備好 – 接口方法

    跳轉到Play頁面 – 把歌曲id帶過進去

    在SongItem.vue - 點擊播放字體圖標

    methods: {playFn(){this.$router.push({path: '/play',query: {id: this.id // 歌曲id, 通過路由跳轉傳遞過去}})} }

    總結: 準備好播放頁, 點擊播放傳歌曲id過去, 到播放頁-再請求響應數據和歌曲地址用audio標簽播放

    1.21 網易云音樂-vant適配

    目標: 切換不同機型, 刷新后看看標簽大小適配嗎

    • postcss – 配合webpack翻譯css代碼
    • postcss-pxtorem – 配合webpack, 自動把px轉成rem
    • 新建postcss.config.js – 設置相關配置
    • 重啟服務器, 再次觀察Vant組件是否適配
  • 下載postcss和postcss-pxtorem@5.1.1

    postcss作用: 是對css代碼做降級處理

    postcss-pxtorem: 自動把所有代碼里的css樣式的px, 自動轉rem

  • src/新建postcss.config.js

  • module.exports = {plugins: {'postcss-pxtorem': {// 能夠把所有元素的px單位轉成Rem// rootValue: 轉換px的基準值。// 例如一個元素寬是75px,則換成rem之后就是2rem。rootValue: 37.5,propList: ['*']}} }

    附全文代碼——與案例代碼實現方法不統一


    api/Home.js

    // 文件名-盡量和模塊頁面文件名統一(方便查找) import request from '@/utils/request' // import axios from 'axios'// 首頁 - 推薦歌單 export const recommendMusic = params => request({url: '/personalized',params// 將來外面可能傳入params的值 {limit: 20} })// 首頁 - 推薦最新音樂 export const newMusic = params => request({url: "/personalized/newsong",params })

    api/index.js

    // api文件夾下 各個請求模塊js, 都統一來到index.js再向外導出 import {recommendMusic, newMusic} from './Home' // import {hotSearch, searchResultList} from './Search' import {getSongById, getLyricById} from './Play'export const recommendMusicAPI = recommendMusic // 請求推薦歌單的方法導出 export const newMusicAPI = newMusic // 首頁 - 最新音樂// export const hotSearchAPI = hotSearch // 搜索 - 熱搜關鍵詞 // export const searchResultListAPI = searchResultList // 搜索 = 搜索結果export const getSongByIdAPI = getSongById // 歌曲 - 播放地址 export const getLyricByIdAPI = getLyricById // 歌曲 - 歌詞數據

    api/Play.js

    import request from '../utils/request'// 播放頁 - 獲取歌曲詳情 export const getSongById = (id) => request({url: `/song/detail?ids=${id}`,method: "GET" })// 播放頁 - 獲取歌詞 export const getLyricById = (id) => request({url: `/lyric?id=${id}`,method: "GET" })

    api/Search.js

    // 搜索模塊 import request from '@/utils/request'// 熱搜關鍵字 export function hotSearch() {return request({// method: 'get'url: '/search/hot'}) }// 搜索結果 export const searchResultList = params => request({url: '/cloudsearch',params })// export const hotSearch = params => request({ // url: '/search/hot', // params // })// 搜索結果 // export const searchResultList = params => request({ // url: '/cloudsearch', // params // })

    剩下的…可以私信我,我打包了,實在貼不動了

    以iphone6為基準, 37.5px為基準值換算rem

    今日總結

    • 掌握vant組件庫的使用 - 找組件, 引組件, 用組件
    • 能夠對vant組件自帶樣式進行覆蓋自定義
    • 遇到重復的標簽, 自己也封裝了一個復用的組件
    • 掌握查詢文檔和使用每個屬性的方式

    總結

    以上是生活随笔為你收集整理的调用网易云Api接口实现移动Web网易云部分功能(搜索+列表+播放)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。