成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Vue 實現(xiàn)的音樂項目 music app 知識點總結(jié)分享

meteor199 / 1465人閱讀

摘要:后兩個屬性可選。屬性定義了項目的縮小比例,默認為,即如果空間不足,該項目將縮小。屬性定義了在分配多余空間之前,項目占據(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-shrinkflex-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í)行 滾動刷新 了

    
  
  
修復(fù)進度條的 BUG

迷你播放器暫停狀態(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

結(jié)語

以上是在實現(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

相關(guān)文章

  • 讓工作與音樂Vue)相伴

    摘要:前言最近在自學(xué)打算自己仿一個項目來實戰(zhàn)一下,由于本人很喜歡聽歌,所以就選擇了網(wǎng)易云音樂,在這與大家分享一下自己所遇到的問題,其中也有些不足之處也希望大家提一些寶貴的意見,互相學(xué)習(xí),一起進步。 showImg(https://segmentfault.com/img/remote/1460000015805758); 前言 最近在自學(xué)vue,打算自己仿一個項目來實戰(zhàn)一下,由于本人很喜歡聽...

    jemygraw 評論0 收藏0
  • ?基于H5+js開發(fā)一款音樂播放器

    前言:當下音樂播放器不勝其數(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...

    BearyChat 評論0 收藏0
  • vue-music(1)音樂播發(fā)器 項目開發(fā)記錄

    摘要:在中新建組件許文瑞正在吃屎。。。。在中添加如下代碼三歌手組件開發(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...

    happen 評論0 收藏0
  • 給自己網(wǎng)站添加網(wǎng)易云音樂歌單吧^ ^

    摘要:每次用網(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 首先我們...

    RaoMeng 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<