【WEB】vue2.0开发音乐播放器

来源:互联网 发布:心理教学软件 编辑:程序博客网 时间:2024/06/06 16:07

1.安装模版

vue init webpack vue-music

注:vue脚手架可以帮我们初始化webpack的配置
初始化后目录如下:
这里写图片描述
//所有开发是基于修改src的文件
api:用来放跟后端请求相关
common:通用的静态资源(fonts/image/js/stylus)
components:通用组件
router:路由相关组件
store:vuex相关代码
App.vue:模版
main.js:文件入口,用来渲染app的文件

stylus文件里的变量定义:

// 颜色定义规范$color-background = #222$color-background-d = rgba(0, 0, 0, 0.3)$color-highlight-background = #333$color-dialog-background = #666$color-theme = #ffcd32$color-theme-d = rgba(255, 205, 49, 0.5)$color-sub-theme = #d93f30$color-text = #fff$color-text-d = rgba(255, 255, 255, 0.3)$color-text-l = rgba(255, 255, 255, 0.5)$color-text-ll = rgba(255, 255, 255, 0.8)//字体定义规范$font-size-small-s = 10px$font-size-small = 12px$font-size-medium = 14px$font-size-medium-x = 16px$font-size-large = 18px$font-size-large-x = 22px

在其他的styl文件中,通过@import “./XX.styl”

在main.js中:import 'common/stylus/index.styl'

注:有几处需要修改:
1.在package.json里,添加stylus和stylus-loader依赖(npm install)
2.在webpack.base.conf.js里,配置路径别名

resolve: {    extensions: ['.js', '.vue', '.json'],    alias: {      '@': resolve('src'),      'common': resolve('src/common'),      'components': resolve('src/components'),      'base': resolve('src/base'),      'api': resolve('src/api')    }  },

启动项目:npm run dev

2.开发tab界面并配置路由

1.components:
新建m-header文件:

<template>  <div class="m-header">    <div class="icon"></div>    <h1 class="text">Chicken Music</h1>    <router-link tag="div" class="mine" to="/user">      <i class="icon-mine"></i>    </router-link>  </div></template><script type="text/ecmascript-6">  export default {}</script><style scoped lang="stylus" rel="stylesheet/stylus">  @import "~common/stylus/variable"  @import "~common/stylus/mixin"  .m-header    position: relative    height: 44px    text-align: center    color: $color-theme    font-size: 0    .icon      display: inline-block      vertical-align: top      margin-top: 6px      width: 30px      height: 32px      margin-right: 9px      bg-image('logo')      background-size: 30px 32px    .text      display: inline-block      vertical-align: top      line-height: 44px      font-size: $font-size-large    .mine      position: absolute      top: 0      right: 0      .icon-mine        display: block        padding: 12px        font-size: 20px        color: $color-theme</style>

在app.vue里面

<template>  <div id="app" @touchmove.prevent>    <m-header></m-header><!--放入模版中-->    <tab></tab>    <keep-alive><!--可以把dom缓存到内存中-->      <router-view></router-view>    </keep-alive>    <player></player>  </div></template><script type="text/ecmascript-6">  import MHeader from 'components/m-header/m-header'//引入包  import Player from 'components/player/player'  import Tab from 'components/tab/tab'  export default {    components: {      MHeader,//引入组件      Tab,      Player    }  }</script><style scoped lang="stylus" rel="stylesheet/stylus"></style>

2.配置router里的index.js

export default new Router({routes:[{    path:'/',    component:Recommend//注意拼写},{    path:'/recommend',    component:Recommend},{    ...}]})

之后引入到vue实例里
在main.js中

/* eslint-disable no-new */new Vue({  el: '#app',  router,  render: h => h(App)})

下一步,是使用这个路由:
在APP.VUE中

<router-view></router-view>

下一步,写导航栏

<template>  <div class="tab">    <router-link tag="div" class="tab-item" to="/recommend">      <span class="tab-link">推荐</span>    </router-link><!--tag标示把它渲染成什么标签,to指向了需要跳转的路由-->    <router-link tag="div" class="tab-item" to="/singer">      <span class="tab-link">歌手</span>    </router-link>    <router-link tag="div" class="tab-item" to="/rank">      <span class="tab-link">排行      </span>    </router-link>    <router-link tag="div" class="tab-item" to="/search">      <span class="tab-link">搜索</span>    </router-link>  </div></template>

加入到app.vue里

<tab></tab>

3.推荐界面开发

1.数据来源:qq音乐。jsonp:
github.com/webmodules/jsonp:学习index.js源码
使用npm 安装
在 common里jsonp.js

import originJsonp from 'jsonp'export default function jsonp(url, data, option) {  url += (url.indexOf('?') < 0 ? '?' : '&') + param(data)  return new Promise((resolve, reject) => {    originJsonp(url, option, (err, data) => {      if (!err) {        resolve(data)      } else {        reject(err)      }    })  })}export function param(data) {  let url = ''  for (var k in data) {    let value = data[k] !== undefined ? data[k] : ''    url += '&' + k + '=' + encodeURIComponent(value)  }  return url ? url.substring(1) : ''}

在api封装recommond.js方法。

export function getRecommend() {  const url = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg'  const data = Object.assign({}, commonParams, {    platform: 'h5',    uin: 0,    needNewCode: 1  })  return jsonp(url, data, options)}

在recommand.vue中

<script type="text/ecmascript-6">  import Slider from 'base/slider/slider'  import Loading from 'base/loading/loading'  import Scroll from 'base/scroll/scroll'  import {getRecommend, getDiscList} from 'api/recommend'  import {playlistMixin} from 'common/js/mixin'  import {ERR_OK} from 'api/config'  import {mapMutations} from 'vuex'  export default {    mixins: [playlistMixin],    data() {      return {        recommends: [],        discList: []      }    },    created() {      this._getRecommend()      this._getDiscList()    },    methods: {      handlePlaylist(playlist) {        const bottom = playlist.length > 0 ? '60px' : ''        this.$refs.recommend.style.bottom = bottom        this.$refs.scroll.refresh()      },      loadImage() {        if (!this.checkloaded) {          this.checkloaded = true          this.$refs.scroll.refresh()        }      },      selectItem(item) {        this.$router.push({          path: `/recommend/${item.dissid}`        })        this.setDisc(item)      },      _getRecommend() {        getRecommend().then((res) => {          if (res.code === ERR_OK) {            this.recommends = res.data.slider          }        })      },      _getDiscList() {        getDiscList().then((res) => {          if (res.code === ERR_OK) {            this.discList = res.data.list          }        })      },      ...mapMutations({        setDisc: 'SET_DISC'      })    },    components: {      Slider,      Loading,      Scroll    }  }</script>

设置轮播图组件:

<template>  <div class="slider" ref="slider">    <div class="slider-group" ref="sliderGroup">      <slot>      </slot><!--插槽,外部我们引入slider,slider包裹的dom会被插入到这个部分。-->    </div>    <div class="dots">      <span class="dot" :class="{active: currentPageIndex === index }" v-for="(item, index) in dots"></span>    </div>  </div></template><script type="text/ecmascript-6">  import {addClass} from 'common/js/dom'  import BScroll from 'better-scroll'  export default {    name: 'slider',    props: {      loop: {        type: Boolean,        default: true      },      autoPlay: {        type: Boolean,        default: true      },      interval: {        type: Number,        default: 4000      }    },    data() {      return {        dots: [],        currentPageIndex: 0      }    },    //初始化操作    mounted() {      setTimeout(() => {        this._setSliderWidth()        this._initDots()        this._initSlider()        if (this.autoPlay) {          this._play()        }      }, 20)      window.addEventListener('resize', () => {        if (!this.slider) {          return        }        this._setSliderWidth(true)        this.slider.refresh()      })    },    activated() {      if (this.autoPlay) {        this._play()      }    },    deactivated() {      clearTimeout(this.timer)    },    beforeDestroy() {      clearTimeout(this.timer)    },    methods: {      _setSliderWidth(isResize) {        this.children = this.$refs.sliderGroup.children        //通过此获得refs        let width = 0        let sliderWidth = this.$refs.slider.clientWidth        for (let i = 0; i < this.children.length; i++) {          let child = this.children[i]          addClass(child, 'slider-item')          child.style.width = sliderWidth + 'px'          width += sliderWidth        }        if (this.loop && !isResize) {          width += 2 * sliderWidth        }        this.$refs.sliderGroup.style.width = width + 'px'      },      _initSlider() {        this.slider = new BScroll(this.$refs.slider, {          scrollX: true,          scrollY: false,          momentum: false,          snap: true,          snapLoop: this.loop,          snapThreshold: 0.3,          snapSpeed: 400        })        this.slider.on('scrollEnd', () => {          let pageIndex = this.slider.getCurrentPage().pageX          if (this.loop) {            pageIndex -= 1          }          this.currentPageIndex = pageIndex          if (this.autoPlay) {            this._play()          }        })        this.slider.on('beforeScrollStart', () => {          if (this.autoPlay) {            clearTimeout(this.timer)          }        })      },      _initDots() {        this.dots = new Array(this.children.length)      },      _play() {        let pageIndex = this.currentPageIndex + 1        if (this.loop) {          pageIndex += 1        }        this.timer = setTimeout(() => {          this.slider.goToPage(pageIndex, 0, 400)        }, this.interval)      }    }  }</script>

recommend.vue

<template>  <div class="recommend" ref="recommend">    <scroll ref="scroll" class="recommend-content" :data="discList">      <div>        <div v-if="recommends.length" class="slider-wrapper" ref="sliderWrapper">          <slider>          <!--遍历-->            <div v-for="item in recommends" v-if="recommends.length>0"><!--当数据加载进来之后,在进行dom填充-->              <a :href="item.linkUrl">                <img class="needsclick" @load="loadImage" :src="item.picUrl">              </a>            </div>          </slider>        </div>        <div class="recommend-list">          <h1 class="list-title">热门歌单推荐</h1>          <ul>            <li @click="selectItem(item)" v-for="item in discList" class="item">              <div class="icon">                <img width="60" height="60" v-lazy="item.imgurl">              </div>              <div class="text">                <h2 class="name" v-html="item.creator.name"></h2>                <p class="desc" v-html="item.dissname"></p>              </div>            </li>          </ul>        </div>      </div>      <div class="loading-container" v-show="!discList.length">        <loading></loading>      </div>    </scroll>    <router-view></router-view>  </div></template><script>    export default{    data(){        return{            recommends:[]        }    },    created(){        this._getRecommend()    },    methods:{        _getRecommend(){            //导入数据            this.recommends=res.data        }    }}</script>

4.歌单界面
后端代理在dev-server.js中

ar apiRoutes = express.Router()apiRoutes.get('/getDiscList', function (req, res) {  var 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)  }).catch((e) => {    console.log(e)  })})

在这里用到了axios库,在浏览器发送https请求。

原创粉丝点击