摘要:后兩個屬性可選。屬性定義了項目的縮小比例,默認為,即如果空間不足,該項目將縮小。屬性定義了在分配多余空間之前,項目占據(jù)的主軸空間。它的默認值為,即項目的本來大小。結(jié)合的異步組件和的代碼分割功能,輕松實現(xiàn)路由組件的懶加載。
項目總結(jié)
這是我第二個用 Vue 實現(xiàn)的項目,下面內(nèi)容包括了在實現(xiàn)過程中所記錄的知識點以及一些小技巧
項目演示地址:https://music-vue.n-y.io
源代碼地址:https://github.com/nanyang24/...
此應(yīng)用的全部數(shù)據(jù)來自 QQ音樂,利用 axios 結(jié)合 node.js 代理后端請求抓取
全局通用的應(yīng)用級狀態(tài)使用 vuex 集中管理
全局引入 fastclick 庫,消除 click 移動瀏覽器 300ms 延遲
頁面是響應(yīng)式的,適配常見的移動端屏幕,采用 flex 布局
疑難總結(jié) & 小技巧 關(guān)于 Vue 知識 & 使用技巧 v-html 可以轉(zhuǎn)義字符,處理特定接口很有用 watch 對象可以觀測 屬性 的變化 像這種父組件傳達子組件的參數(shù)通常都是在data()里面定義的,為什么這里要放到created()定義,兩者有什么區(qū)別呢?因為這個變量不需要觀測它的變化,因此不用定義在 data 里,這樣也會對性能有所優(yōu)化
不明白什么時候要把變量放在data()里,什么時候又不需要放 ?需要監(jiān)測這個數(shù)據(jù)變化的時候,放在 data() 里,會給數(shù)據(jù)添加 getter 和 setter
生命周期 鉤子函數(shù)生命周期鉤子函數(shù),比如 mounted 是先觸發(fā)子組件的 mounted,再會觸發(fā)父組件的 mounted,但是對于 created 鉤子,又會先觸發(fā)父組件,再觸發(fā)子組件。
銷毀計數(shù)器如果組件有計數(shù)器,在組件銷毀時期要記得清理,好習(xí)慣
對于 Vue 組件,this.$refs.xxx 拿到的是 Vue 實例,所以需要再通過 $el 拿到真實的 dom 關(guān)于 JS 知識 & 技巧 setTimeout(fn, 20)一般來說 JS 線程執(zhí)行完畢后一個 Tick 的時間約17ms內(nèi) DOM 就可以渲染完畢所以課程中 setTimeout(fn, 20) 是非常穩(wěn)妥的寫法
關(guān)于 webpack 知識 & 技巧 " ~ " 使 SCSS 可以使用 webpack 的相對路徑@import "~common/scss/mixin"; @import "~common/scss/variable";babel-runtime 會在編譯階段把 es6 語法編譯的代碼打包到業(yè)務(wù)代碼中,所以要放在dependencies里。 Fast Click 是一個簡單、易用的庫,專為消除移動端瀏覽器從物理觸摸到觸發(fā)點擊事件之間的300ms延時 為什么會存在延遲呢?
從觸摸按鈕到觸發(fā)點擊事件,移動端瀏覽器會等待接近300ms,原因是瀏覽器會等待以確定你是否執(zhí)行雙擊事件
何時不需要使用FastClick 不會伴隨監(jiān)聽任何桌面瀏覽器
Android 系統(tǒng)中,在頭部 meta 中設(shè)置 width=device-width 的Chrome32+ 瀏覽器不存在300ms 延時,所以,也不需要
同樣的情況也適用于 Android設(shè)備(任何版本),在viewport 中設(shè)置 user-scalable=no,但這樣就禁止縮放網(wǎng)頁了
IE11+ 瀏覽器中,你可以使用 touch-action: manipulation; 禁止通過雙擊來放大一些元素(比如:鏈接和按鈕)。IE10可以使用 -ms-touch-action: manipulation
請求接口jsonp:
XHR:
手寫輪播圖利用 BScroll
BScroll 設(shè)置 loop 會自動 clone 兩個輪播插在前后位置
如果輪播循環(huán)播放,是前后各加一個輪播圖保證無縫切換,所以需要再加兩個寬度
if (this.loop) { width += 2 * sliderWidth }
初始化 dots 要在 BScroll 克隆插入兩個輪播圖之前
dots active狀態(tài) 是通過判斷 currentIndex 與 index 是否相等
currentIndex 更新是通過獲取 scroll 當前 page,BScroll 提供了 api 方便調(diào)用
this.currentPageIndex = this.scroll.getCurrentPage().pageX
為了保證改變窗口大小依然正常輪播,監(jiān)聽窗口 resize 事件,重新渲染輪播圖
window.addEventListener("resize", () => { if (!this.scroll || !this.scroll.enabled) return clearTimeout(this.resizeTimer) this.resizeTimer = setTimeout(() => { if (this.scroll.isInTransition) { this._onScrollEnd() } else { if (this.autoPlay) { this._play() } } this.refresh() }, 60) })
在切換 tab 相當于 切換了 keep-alive 的組件
輪播會出問題,需要手動幫助執(zhí)行,利用了 activated , deactivated 鉤子函數(shù)
activated() { this.scroll.enable() let pageIndex = this.scroll.getCurrentPage().pageX this.scroll.goToPage(pageIndex, 0, 0) this.currentPageIndex = pageIndex if (this.autoPlay) { this._play() } }, deactivated() { this.scroll.disable() clearTimeout(this.timer) }
實測,首次打開網(wǎng)頁并不會執(zhí)行 activated,只有在之后切換 tab ,切回來才會執(zhí)行
在組件銷毀之前 beforeDestroy 銷毀定時器是好習(xí)慣,keep-alive 因為是將組件緩存了,所以不會觸發(fā)
beforeDestroy() { this.scroll.disable() clearTimeout(this.timer) }后端接口代理
簡單設(shè)置一下 Referer, Host,讓別人直接通過瀏覽器抓到你的接口
但是這種方式防不了后端代理的方式
前端 XHR 會有跨域限制,后端發(fā)送 http 請求則沒有限制,因此可以偽造請求
axios 可以在瀏覽器端發(fā)送 XMLHttpRequest 請求,在服務(wù)器端發(fā)送 http 請求
(在項目編寫階段,可以將后端代理請求寫在 webpack 的 dev 文件的 before 函數(shù)內(nèi))
before(app) { app.get("/api/getDiscList", function (req, res) { const url = "https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg" axios.get(url, { headers: { referer: "https://c.y.qq.com/", host: "c.y.qq.com" }, params: req.query }).then((response) => { res.json(response.data) // axios 返回的數(shù)據(jù)在 response.data,要把數(shù)據(jù)透傳到我們自定義的接口里面 res.json(response.data) }).catch((e) => { console.log(e) }) }); }
定義一個路由,get 到一個 /api/getDiscList 接口,通過 axios 偽造 headers,發(fā)送給QQ音樂服務(wù)器一個 http 請求,還有 param 參數(shù)。
得到服務(wù)端正確的響應(yīng),通過 res.json(response.data) 返回到瀏覽器端
另外 因為是 http 請求數(shù)據(jù),是ajax,所以 format 參數(shù)要將原本接口的 jsonp 改為 json
大公司怎么防止被惡意代理呢?當你的訪問量大的時候,出口ip會被查到獲取封禁,還有一種就是參數(shù)驗簽,也就是請求人家的數(shù)據(jù)必須帶一個簽名參數(shù),然后這個簽名參數(shù)是很難拿到的這個正確的簽名,從而達到保護數(shù)據(jù)的目的
當然,獲取的數(shù)據(jù)并不能直接拿來用,需要做進一步的規(guī)格化,達到我們使用的要求,所以在這方面多帶帶封裝了一個 class 來處理這方面的數(shù)據(jù),具體請看src/common/js/song.js
flex 布局,熱門歌單推薦左側(cè) icon 固定大小,flex: 0 0 60px
flex 屬性是 flex-grow , flex-shrink 和 flex-basis 的簡寫,默認值為 0 1 auto。后兩個屬性可選。
flex-grow 屬性定義項目的放大比例,默認為 0,即如果存在剩余空間,也不放大。
flex-shrink 屬性定義了項目的縮小比例,默認為 1,即如果空間不足,該項目將縮小。
flex-basis 屬性定義了在分配多余空間之前,項目占據(jù)的主軸空間(main size)。瀏覽器根據(jù)這個屬性,計算主軸是否有多余空間。它的默認值為auto,即項目的本來大小。
右側(cè) text 區(qū)塊 自適應(yīng)占據(jù)剩下的空間,并且內(nèi)部也采用 flex,使用 flex-direction: column; justify-content: center; 來達到縱向居中排列
recommend 頁面 利用 BScroll 滾動Scroll 初始化但卻沒有滾動,是因為初始化時機不對,必須保證數(shù)據(jù)到來,DOM 成功渲染之后 再去進行初始化
可以使用父組件 給 Scrol組件傳 :data 數(shù)據(jù),Scroll 組件自己 watch 這個 data,有變化就立刻 refesh 滾動
新版本 BScroll 已經(jīng)自己實現(xiàn)檢測 DOM 變化,自動刷新,大部分場景下無需傳 data 了
所以也就 無需監(jiān)聽 img 的 onload 事件 然后執(zhí)行 滾動刷新 了
迷你播放器暫停狀態(tài),進入全屏,按鈕在進度條最左邊
原因:當播放器最小化的時候,progress-bar 仍然在監(jiān)聽 percent 的變化,所以在不斷計算進度條的位置,然而這個時候由于播放器隱藏,進度條的寬度 this.$refs.progressBar.clientWidth 計算為0,因此計算出來的 offset 也是不對的,導(dǎo)致再次最大化播放器的時候,由于播放器是暫停狀態(tài), percent 并不會變化,也不會重新計算這個 offset ,導(dǎo)致 Bug。
解決方案:當播放器最大化的時候,手動去計算一次 offset,確保進度條的位置正確。
progress-bar 組件要 watch 下 fullScreen,在進入全屏的時候調(diào)用一下 移動按鈕函數(shù)
歌詞 lyric獲取歌詞,雖然我們約定返回數(shù)據(jù)是 json,但QQ音樂 返回的是依然是 jsonp,所以我們需要做一層數(shù)據(jù)的處理
const reg = /^w+(({.+}))$/
就是將返回的jsonp格式摘取出我們需要的json字段
ret = JSON.parse(matches[1])
將正則分組(就是正則括號內(nèi)的內(nèi)容)捕獲的json字符串數(shù)據(jù) 轉(zhuǎn)成 json 格式
然后我們在 player 組件中監(jiān)聽 currentSong 的變化,獲取 this.currentSong.getLyric()
axios.get(url, { headers: { referer: "https://c.y.qq.com/", host: "c.y.qq.com" }, params: req.query }).then((response) => { let ret = response.data if (typeof ret === "string") { const reg = /^w+(({.+}))$/ const matches = ret.match(reg) if (matches) { ret = JSON.parse(matches[1]) } } res.json(ret) })
然后我們得到的返回數(shù)據(jù)的是 base64 的字符串,需要解碼,這里用到了第三方庫: js-base64
(我們這次用的是QQ音樂pc版的歌詞,需要解碼base64,而移動版的QQ音樂是不需要的)
this.lyric = Base64.decode(res.lyric)
之后利用第三方庫: js-lyric ,解析我們的歌詞,生成方便操作的對象
getLyric() { this.currentSong.getLyric() .then(lyric => { this.currentLyric = new Lyric(lyric) }) }歌詞滾動
當前歌曲的歌詞高亮是利用 js-lyric 會派發(fā)的 handle 事件
this.currentLyric = new Lyric(lyric, this.handleLyric)
js-lyric 會在每次改變當前歌詞時觸發(fā)這個函數(shù),函數(shù)的參數(shù)為 當前的 lineNum 和 txt
而 使當前高亮歌詞保持最中間 是利用了 BScroll 滾動至高亮的歌詞let middleLine = isIphoneX() ? 7 : 5 // 鑒于iphonex太長了,做個小優(yōu)化 if (lineNum > middleLine) { let lineEl = this.$refs.lyricLine[lineNum - middleLine] this.$refs.lyricList.scrollToElement(lineEl, 1000) } else { this.$refs.lyricList.scrollTo(0, 0, 1000) }cd 與 歌詞 之間滑動
通過監(jiān)聽 middle 的 三個 touch 事件
offsetWidth 是為了計算歌詞列表的一個偏移量的,首先它的偏移量不能大于0,也不能小于 -window.innerWidth。
left 是根據(jù)當前顯示的是 cd 還是歌詞列表初始化的位置,如果是 cd,那么 left 為 0 ,歌詞是從右往左拖的,deltaX 是小于 0 的,所以最終它的偏移量就是 0+deltaX;如果已經(jīng)顯示歌詞了,那么 left 為 -window.innerWidth,歌詞是從左往右拖,deltaX 是大于 0 的,所以最終它的偏移量就是 -window.innerWidth + deltaX。
middleTouchStart(e) { this.touch.initiated = true this.touch.startX = e.touches[0].pageX this.touch.startY = e.touches[0].pageY }, middleTouchMove(e) { if (!this.touch.initiated) return const deltaX = e.touches[0].pageX - this.touch.startX const deltaY = e.touches[0].pageY - this.touch.startY if (Math.abs(deltaY) > Math.abs(deltaX)) { return } const left = this.currentShow === "cd" ? 0 : -window.innerWidth const offsetWidth = Math.min(0, Math.max(-window.innerWidth, left + deltaX)) this.touch.percent = Math.abs(offsetWidth / window.innerWidth) console.log(this.touch.percent) this.$refs.lyricList.$el.style[transform] = `translate3d(${offsetWidth}px,0,0)` this.$refs.lyricList.$el.style[transitionDuration] = 0 this.$refs.middleL.style.opacity = 1 - this.touch.percent this.$refs.middleL.style[transitionDuration] = 0 }, middleTouchEnd() { let offsetWidth, opacity // 從右向左滑 的情況 if (this.currentShow === "cd") { if (this.touch.percent > 0.1) { offsetWidth = -window.innerWidth opacity = 0 this.currentShow = "lyric" } else { offsetWidth = 0 opacity = 1 } } else { // 從左向右滑 的情況 if (this.touch.percent < 0.9) { offsetWidth = 0 opacity = 1 this.currentShow = "cd" } else { offsetWidth = -window.innerWidth opacity = 0 } } const durationTime = 300 this.$refs.lyricList.$el.style[transform] = `translate3d(${offsetWidth}px,0,0)` this.$refs.lyricList.$el.style[transitionDuration] = `${durationTime}ms` this.$refs.middleL.style.opacity = opacity this.$refs.middleL.style[transitionDuration] = `${durationTime}ms` }優(yōu)化
Vue 按需加載路由:
當打包構(gòu)建應(yīng)用時,Javascript 包會變得非常大,影響頁面加載。如果我們能把不同路由對應(yīng)的組件分割成不同的代碼塊,然后當路由被訪問的時候才加載對應(yīng)組件,這樣就更加高效了。
結(jié)合 Vue 的異步組件和 Webpack 的代碼分割功能,輕松實現(xiàn)路由組件的懶加載。
首先,可以將異步組件定義為返回一個 Promise 的工廠函數(shù) (該函數(shù)返回的 Promise 應(yīng)該 resolve 組件本身):
const Foo = () => Promise.resolve({ /* 組件定義對象 */ })
第二,在 Webpack 2 中,我們可以使用動態(tài) import語法來定義代碼分塊點 (split point):
import("./Foo.vue") // 返回 Promise
在我們的項目中的 router/index.js 是這樣定義的:
// Vue 異步加載路由 // 引入5個 一級路由組件 const Recommend = () => import("components/recommend/recommend") const Singer = () => import("components/singer/singer") const Rank = () => import("components/rank/rank") const Search = () => import("components/search/search") const UserCenter = () => import("components/user-center/user-center") // 二級路由組件 const SingerDetail = () => import("components/singer-detail/singer-detail") const Disc = () => import("components/disc/disc") const TopList = () => import("components/top-list/top-list")
無需改動其他的代碼
手機聯(lián)調(diào)電腦,手機 同一WIFI下
配置 config 的 index.js 里的 host 為 "0.0.0.0",手機可以打開電腦的IP地址+端口查看
mac下 ifconfig 查看ip
移動端調(diào)試工具移動端console:vConsole
移動端抓包工具:charles
以上是在實現(xiàn)這個音樂 Vue 項目中遇到的難點以及一些使用技巧。在這里記錄下來方便以后自己查閱,還能夠給同樣在前端這個小領(lǐng)域奮斗的大家提供一小些學(xué)習(xí)資料~
我的 Github:https://github.com/nanyang24
如果對你有幫助,歡迎 star 和 互粉 ~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107496.html
摘要:前言最近在自學(xué)打算自己仿一個項目來實戰(zhàn)一下,由于本人很喜歡聽歌,所以就選擇了網(wǎng)易云音樂,在這與大家分享一下自己所遇到的問題,其中也有些不足之處也希望大家提一些寶貴的意見,互相學(xué)習(xí),一起進步。 showImg(https://segmentfault.com/img/remote/1460000015805758); 前言 最近在自學(xué)vue,打算自己仿一個項目來實戰(zhàn)一下,由于本人很喜歡聽...
前言:當下音樂播放器不勝其數(shù),為了更好的掌握一些東西,我們來自己制作一個音樂播放器。 文章目錄: 一.開發(fā)環(huán)境:二.頁面視圖:1.主文件入口(首頁):2.音樂播放界面: 三.功能實現(xiàn)(1)、index.html:(2)、播放音樂(music.html):(3)、樣式文件(index.css): 四.項目地址: 一.開發(fā)環(huán)境: 開發(fā)工具:HbuliderX; 框架:Vant,Mui,V...
摘要:在中新建組件許文瑞正在吃屎。。。。在中添加如下代碼三歌手組件開發(fā)歌手首頁開發(fā)數(shù)據(jù)獲取數(shù)據(jù)獲取依舊從音樂官網(wǎng)獲取歌手接口創(chuàng)建我們和以前一樣,利用我們封裝的等發(fā)放,來請求我們的接口,返回給。 Vue-Music 跟學(xué)一個網(wǎng)課老師做的仿原生音樂APP跟學(xué)的筆記,記錄點滴,也希望對學(xué)習(xí)vue初學(xué)小伙伴有點幫助 showImg(https://segmentfault.com/img/remot...
摘要:每次用網(wǎng)易云音樂客戶端播放聽歌的時候,收藏的歌曲,在我的博客上也可以同步進行更新。 最近應(yīng)該發(fā)現(xiàn),我的博客https://blog.codelabo.cn左下角多了一個音樂播放器 showImg(https://segmentfault.com/img/remote/1460000016786096?w=1806&h=952); 這個是怎么實現(xiàn)的?一起來看看吧 APlayer 首先我們...
閱讀 2685·2023-04-25 15:15
閱讀 1326·2021-11-25 09:43
閱讀 1613·2021-11-23 09:51
閱讀 1089·2021-11-12 10:36
閱讀 2891·2021-11-11 16:55
閱讀 966·2021-11-08 13:18
閱讀 736·2021-10-28 09:31
閱讀 2060·2019-08-30 15:47