基于Vue2.x开发的音乐播放器app(推荐界面+懒加载+axios获取后端接口实现)
1、项目开发需求分析:
包含四个层面——
(1)推荐模块
(2)歌手模块
(3)排行模块
(4)搜索模块
2、项目开发流程
(1)搭建项目:借助vue-cli脚手架工具,具体请参考博客: …;
由于项目存放在本地电脑E盘VueTest目录下,
cd E:\VueTest
图1:切换项目存放目录、vue-cli搭建初始项目
通过脚手架vue-cli工具搭建项目
vue init webpack vue-music
选择配置项目相关webpack
(2)进入项目vue-music
cd vue-music
安装项目node_mudules依赖(在这里采用淘宝镜像cnpm,可加快安装效率)
cnpm install
上述流程具体操作见(图2:安装项目依赖)
图2:安装项目依赖
在本地加载该项目工程文件
npm run dev
图3:搭建成功界面
当看到localhost:8080/8081时,说明项目工程环境搭建成功。
图4:本地搭建环境成功
当显示上述界面,则表示vue-cli脚手架搭建的vue-music项目成功。
(3)在编辑器中打开该项目工程文件(在该环节中我采用的编辑器:VS Code)
项目目录中所以开发主要基于src目录作为主载
图5:项目工程目录
2.3-1——目录src
api:主要存放后台请求相关的代码,包括ajax,jsonp等,
有.gitkeep文件表示虽为空,但可上传至git上
common:主要存放通用的静态资源,包括字体图标fonts、图片images、脚本文件js库、样式文件stylus
stylus——
base.styl:基础样式,引用variable.styl
variable.styl:以“变量定义”方式引入样式文件(颜色、字体定义规范)
icon.styl:字体图标样式文件
index.styl:引入reset、base、icon三者样式文件
mixin.styl:定义一些函数,方便组件.vue文件中引用
reset.styl:重置样式文件
图6:common目录下stylus
components:主要存放项目中组件化.vue文件
router:主要存放静态路由相关代码index.js
store:主要存放vuex相关代码
App.vue:
main.js:主要存放渲染app文件的js脚本
2.3-2:更改相关文件
在build文件夹下,webpack.base.conf.js中
‘.js’,
‘.vue’,
‘.json’],
:
resolve(
‘src’),
:
resolve(
‘src/common’)
这样,在项目中可以这么使用,如下:
‘common/stylus/index.styl’
3、页面骨架开发
(1)页面入口+header
首先,package.json文件做以下更改
“^6.26.0”,
“^2.5.2”,
“^3.0.1”,
“^1.0.6”
在“devDependencis”下,新增下面babel-polyfill、stylus、stylus-loader三个依赖,
运行cnpm install
“:
“^0.54.5”,
“^6.2.0”,
“^3.0.1”
(2)路由配置+顶导组件开发
router静态路由——根组件默认指向
vue-router(index.js)——new Vue({ router })(main.js)——<router-view/>(App.vue)
main.js——babel-polyfill、fastClick
在http://localhost:8080/#中可以用静态路由vue-router实现界面间切换
图7:vue-router实现界面跳转
解析:由于在项目工程文件中指定根路径path:/recommend,所以默认为该界面,同时点击不同的tab选项列 表,可由静态路由进入不同的组件。
Router
from
‘vue-router’
use(
Router)
default
new
Router({
‘/’,
Recommend
‘/recommend’,
Recommend
‘/singer’,
Singer
‘/rank’,
Rank
‘/search’,
Search
4、推荐模块开发
locahost:8080/#/recommend
疑难点:
a、数据获取——线上真实数据,非模拟。用qq音乐进行抓取数据,(采用jsonp进行抓取线上数据)
图8:qq音乐播放器抓取数据
b、jsonp原理:可参考github地址(https://github.com/webmodules/jsonp)
图9:github-jsonp地址
c、在index.js可查看具体实现原理
(1)Install
Install for node.js or browserify usingnpm
:
$ npm install jsonp
Install for component(1) using component
:
$ component install LearnBoost/jsonp
Install for browser using bower
:
$ bower install jsonp
(2)API
jsonp(url, opts, fn)
url
(String
) url to fetchopts
(Object
), optionalparam
(String
) name of the query string parameter to specify the callback (defaults tocallback
)timeout
(Number
) how long after a timeout error is emitted.0
to disable (defaults to60000
)prefix
(String
) prefix for the global callback functions that handle jsonp responses (defaults to__jp
)name
(String
) name of the global callback functions that handle jsonp responses (defaults toprefix
+ incremented counter)
fn
callback
操作——在package.json下定义jsonp,再cnpm install安装该依赖
“^6.26.0”,
“^2.5.2”,
“^3.0.1”,
“^1.0.6”,
“^0.2.1”
jsonp的封装“./src/common/js/jsonp.js”
originJSONP
from
‘jsonp’
default
function
jsonp(
url,
data,
option) {
url.
indexOf(
‘?’) <
0 ?
‘?’ :
‘&’) +
param(
data)
new
Promise((
resolve,
reject)
=> {
url,
option, (
err,
data)
=> {
err) {
data)
else {
err)
param(
data) {
url =
”
var
k
in
data) {
value =
data[
k] !==
undefined ?
data[
k] :
”
`&
${
k
}
=
${
encodeURIComponent(
value)
}
`
url ?
url.
substring(
1) :
”
jsonp使用:对其以promise方式封装,同时在/api目录下封装获取数据的方法
(1)轮播图
4.1-1数据抓取
components——recommend.vue“slide-wrapper”
api——recommend.js、config.js
js——jsonp.js
当上述文件编译成功,在开发者工具F12下的console控制台程序,会显示如下:
图10:qq音乐轮播图数据抓取成功界面
4.1-2:轮播图组件开发
目录base基础目录——src/base/slider/slider.vue,
slot插槽<slot></slot>
div
class=
“slider”
>
div
class=
“slider-group”
>
slot
></
slot
>
div
>
div
class=
“dots”
>
div
>
div
>
轮播图模块实现:
实现方式很多,在这里借助better-scroll库实现Slider组件开发
属性:snap——false,针对slider开发,普通列表滚动不需要配置
snap——false 是否可以无缝循环轮播
snapSpeed——400,轮播图切换的动画时间
4.1-2-1:slider-group(轮播图横向点击无缝滚动)
a、初始化Scroll
首先,在package.json中安装依赖,并在命令行中执行“cnpm install”
“^1.4.2”,
其次,在slider.vue组件中引入该库
BScroll
from
“better-scroll”
b、计算setSliderWidth,在methods方法中,定义_setSliderWidth()
children =
this.
$refs.
sliderGroup.
children
width =
0
sliderWidth =
this.
$refs.
slider.
clientWidth
let
i=
0;
i<
this.
children.
length;
i++) {
child =
this.
children[
i]
child,
‘slider-item’)
style.
width =
sliderWidth +
‘px’
sliderWidth
this.
loop) {
2*
sliderWidth
$refs.
sliderGroup.
style.
width =
width +
‘px’
c、初始化轮播图slider,methods中_initSlider()方法,此时引用a中BScroll插件
slider =
new
BScroll(
this.
$refs.
slider, {
true,
false,
false,
// 惯性
true,
//无缝滚动
this.
loop,
0.3,
400,
true
4.1-2-2:dots设置自动轮播
为了实现该功能,必须在初始化initSlider()之前初始化dos,
首先,在data()中定义一个dots对象
其次,在methods方法中定义_initDots()
dots =
new
Array(
this.
children.
length)
最后,在mounted钩子里调用该方法,在_initSlider()之前
=> {
_setSliderWidth()
_initDots()
_initSlider()
20);
测试:当不清除定时器时,由于在自动播放autoPlay()之前,为手动播放,所以需要清除clearInteral()
slider.
on(
‘scrollEnd’, ()
=> {
pageIndex =
this.
slider.
getCurrentPage().
pageX
this.
loop) {
1
currentPageIndex =
pageIndex
this.
autoPlay) {
this.
timer)
_play()
运行效果图如下:(注意:可以自动播放,截取的是静态图)
优化: 当发现在控制台上切换不同的屏幕大小的时候,界面显示出现错误,如下:
解析:上述bug出现的主要原因是因为_setSliderWidth()中sliderWidth出现了小问题,即window下的resize
解决办法:监听一个window下resize事件
addEventListener(
‘resize’, ()
=> {
this.
slider) {
_setSliderWidth(
true)
slider.
refresh()
此外,在_setSliderWidth()事件,应该传入参数isResize做判断,同时执行函数时,初始下不需要isResize(),但到重新计算宽度时,就不能width*2
this.
loop && !
isResize) {
2*
sliderWidth
重新编译后,会发现无论是在移动端进行测试,还是切换屏幕大小,在pc或Mac端测试,宽度均不会出现问题,
但是,当我们重新切换或者加载的时候,recommend.vue生命周期,以及相关数据均会重新加载一遍才会刷新到数据,导致体验差,
优化1:App.vue中,将router-view加一个keep-alive进行dom缓存
keep-alive
>
router-vxew
></
router-view
>
keep-alive
>
优化2:slider.vue中,export default({})加一个destroyed()清除缓存定时器
this.
timer)
4.1-3:歌单列表组件开发
a、歌单数据接口分析
在这里抓取的是PC端歌单数据,地址:https://y.qq.com/portal/playlist.html
从上述console控制台下NetWork general下的requestUrl即为表单数据接口地址
https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg
b、实现原理
首先,定义接口获取函数。包括url以及data相关数据,在recommend.js
function
getDiscList() {
url =
‘https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg’
data =
Object.
assign({}.
commonParams, {
‘yqq’,
0,
0,
10000000,
5,
0,
29,
Math.
random()
jsonp(
url,
data,
options)
其次,在recommend.vue组件下调用该获取接口数据函数
_getRecommend(),
_getDiscList()
同时,在表头引入该函数
getRecommend,
getDiscList}
from
‘api/recommend’
c、后端接口代理axios
由于若使用jsonp进行数据抓取,会导致一些错误,因为qq音乐服务器端不会识别,只能在项目build目录下dev-server.js下,配置与qq音乐相同的headers——referer、host相同,这样服务器端识别完成,相当于后端代理的方法获取了后台的歌单接口数据。
采用ajax http请求API axios库https://github.com/mzabriskie/axios
首先,package.json上写入axios,并在cmd命令行cnpm install安装依赖
“^0.17.1”
在build/dev-server.js中中,设置Axios后端接口代理,用于获取接口数据,再进行调取相应的api。
axios =
require(
‘axios’)
app =
express()
apiRoutes =
express.
Router()
get(
‘/getDiscList’,
function(
req,
res) {
url =
‘https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg’
get(
url, {
‘https://c.y.qq.com/’,
‘c.y.qq.com’
req.
query
then((
response)
=> {
json(
response.
data)
catch((
e)
=> {
log(
e)
use(
‘/api’,
apiRoutes)
未完待续…(由于axios请求接口一直处于404失败状态,所以recommend组件结合dev.server.js配置未达到预期的效果,还需要做到后期的改善)
4.1-4:scroll滚动组件,scroll.vue
由于scroll滚动适用于所有模块界面的滑动,所以作为一个组件进行开发,结合slot插槽进行使用
template
>
div
ref=
“wrapper”
>
slot
></
slot
>
div
>
template
>
在<script/>文件主要用于设置文件所需的脚本渲染,包括import、export模块的引入,再加上属性之间的渲染,
this.
$refs.
wrapper) {
scroll =
new
BScroll(
this.
$refs.
wrapper, {
this.
probeType,
this.
click
scroll &&
this.
scroll.
enable()
scroll &&
this.
scroll.
disable()
scroll &&
thid.
scroll.
refresh()
初始化完_initScroll()方法后,需要在mounted()函数中使用,
=> {
_initScroll()
20)
其次,采用watch进行监听数据data变化
=> {
refresh()
20)
4-3:图片懒加载
与图片“延迟加载”的js、jq实现原理一致:
1 设置一个定时器,计算每张图片是否会随着滚动条的滚动,而出现在视口(也就是浏览器中的 展现网站的空白部分 )中;
2 为<img>标签设置一个暂存图片URL的自定义属性(例如loadpic),当图片出现在视口时,再将loadpic的值赋给图片的src属性;
优点:(1)节省流量
(2)提升加载速度
引用第三方插件:vue-lazyload插件
https://github.com/523451928/vue-lazyload
使用方法
- 1、直接页面引用vue-lazyload.js(注意我自己使用的vue-cli,所以在js文件里面最后导出了Lazyload),页面引用需要删除export default Lazyload
- 2、用脚手架import js文件
html 部分:
<img :imgsrc="url"/>
JS 部分:
-
//vue-cli
-
import Lazyload from ‘vue-lazyload.js’
-
Vue.use(Lazyload)
-
-
//页面直接引用
-
Vue.use(Lazyload)
-
-
this.$lazyload({
-
elm:document.querySelector(), //需要懒加载的图片集合 (默认所有图片)
-
src:“imgsrc”, //给img标签加的属性为图片的地址 (默认imgsrc)
-
threshold:100, //提前加载距离 (默认100px)
-
opa:0.3, //图片初始透明度 (默认0.3)
-
duration:1.5, //过渡时间
-
loadImg:” //加载之前显示的load图片的路径
-
})
VueLazyLoad
from
‘vue-lazyload’
use(
VueLazyLoad, {
require(
‘common/image/default.png’)