当前位置:   article > 正文

Vue3.0项目——打造企业级音乐App(一)Tab栏、轮播图、歌单列表、滚动组件_vue3开发企业级音乐web app

vue3开发企业级音乐web app

系列文章目录

内容参考链接
Vue3.0 项目启动Vue3.0 项目启动(打造企业级音乐App)
Vue3.0项目——打造企业级音乐App(一)Tab栏、轮播图、歌单列表、滚动组件
Vue3.0项目——打造企业级音乐App(二)图片懒加载、v-loading指令的开发和优化


项目演示

vue3.0-music

设置移动端限制

该项目为移动端的项目,我们要设置缩放比例为 1,并且禁止用户双击缩放。

在这里插入图片描述

引入全局样式文件

在 main.js 中引入样式文件

import '@/assets/scss/index.scss'
  • 1

Tab 组件实现

总体效果图:

在这里插入图片描述

header.vue 组件

  • 设置头部组件的内容,样式
  • 两个 icon,一个名字
<template>
  <div class="header">
    <span class="icon"></span>
    <h1 class="text">Joyful Music</h1>
    <router-link class="mine" to="/user">
      <i class="icon-mine"></i>
    </router-link>
  </div>
</template>

<script>
  export default {
    name: 'm-header'
  }
</script>

<style lang="scss" scoped>...</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

tab 组件

<template>
  <div class="tab">
    <router-link
      class="tab-item"
      v-for="tab in tabs"
      :key="tab.path"
      :to="tab.path"
    >
      <span class="tab-link">
        {{tab.name}}
      </span>
    </router-link>
  </div>
</template>

<script>
  export default {
    name: 'tab',
    data() {
      return {
        tabs: [
          {
            name: '推荐',
            path: '/recommend'
          },
          {
            name: '歌手',
            path: '/singer'
          },
          {
            name: '排行',
            path: '/top-list'
          },
          {
            name: '搜索',
            path: '/search'
          }
        ]
      }
    }
  }
</script>

<style lang="scss" scoped>...</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

在这里插入图片描述

App.vue 组件

<template>
  <m-header></m-header>
  <tab></tab>
  <router-view></router-view>
</template>

<script>
import Header from '@/components/header/header.vue'
import Tab from '@/components/tab/tab.vue'

export default {
  components: {
    MHeader: Header,
    Tab
  }
}
</script>

<style lang="scss"></style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

./router/index.js 文件

import { createRouter, createWebHashHistory } from 'vue-router'
import Recommend from '@/views/recommend'
import Singer from '@/views/singer'
import TopList from '@/views/top-list'
import Search from '@/views/search'

const routes = [
  {
    path: '/',
    redirect: '/recommend' // 重定向
  },
  {
    path: '/recommend',
    component: Recommend
  },
  {
    path: '/singer',
    component: Singer
  },
  {
    path: '/top-list',
    component: TopList
  },
  {
    path: '/search',
    component: Search
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

获取轮播图接口数据

./server/base.js 文件

  • 封装 axios
import axios from 'axios'

const ERR_OK = 0
// 开发环境下这样定义baseURL
const baseURL = '/'

axios.defaults.baseURL = baseURL

export function get(url, params) {
    return axios.get(url, {
        params
    }).then((res) => {
        const serverData = res.data
        if (serverData.code === ERR_OK) {
            return serverData.result
        }
    }).catch((e) => {
        console.log(e)
    })
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

./server/recommend.js 文件

  • 获取轮播图接口
  • ./base 里面是后端的一些配置
import { get } from './base'

export function getRecommend() {
    return get('/api/getRecommend')
}
  • 1
  • 2
  • 3
  • 4
  • 5

recommend.vue 组件

  • 把刚刚的轮播图文件导入进来
  • async await 异步处理,打印获取的结果
<template>
  <div class="recommend">
    推荐页面
  </div>
</template>

<script>
  import { getRecommend } from '@/service/recommend'
  export default {
    name: 'recommend',
    async created() {
      const result = await getRecommend()
      console.log(result)
    }
  }
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述

推荐页轮播图

轮播图效果使用的是 BetterScroll 2.0 参考链接

在这里插入图片描述

./base/slider/use-slider.js 文件

  • 导入轮播图核心滚动和滑动栏
  • 定义滑动栏和当前页
  • new BScroll() 可以接收多个参数,第二个参数可以是对象的形式,里面添加多个参数
import BScroll from '@better-scroll/core'
import Slide from '@better-scroll/slide'

import { onMounted, onUnmounted, ref } from 'vue'

BScroll.use(Slide)

export default function useSlider(wrapperRef) {
    const slider = ref(null)
    const currentPageIndex = ref(0)
    onMounted(() => {
        const sliderVal = slider.value = new BScroll(wrapperRef.value, {
            click: true,
            scrollX: true, // 横向滚动
            scrollY: false,
            momentum: false, // 避免惯性动画带来的快速滚动时的闪烁的问题和快速滑动时一次滚动多页的问题
            bounce: false, // 避免在循环衔接的时候出现闪烁
            probeType: 2, // 在用户拖动 slide 时,实时获取到 slide 的 PageIndex 的改变,需要设置为 2 or 3
            slide: true
        })

        sliderVal.on('slideWillChange', (page) => {
            currentPageIndex.value = page.pageX
        })
    })

    // 销毁
    onUnmounted(() => {
        slider.value.destroy()
    })

    return {
        slider,
        currentPageIndex
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

./component/base/slider/slider.vue 组件

  • 渲染图片和滚动条
<template>
  <div class="slider" ref="rootRef">
    <div class="slider-group">
      <div
        class="slider-page"
        v-for="item in sliders"
        :key="item.id"
      >
        <a :href="item.link">
          <img :src="item.pic"/>
        </a>
      </div>
    </div>
    <div class="dots-wrapper">
      <span
        class="dot"
        v-for="(item, index) in sliders"
        :key="item.id"
        :class="{'active': currentPageIndex === index}">
      </span>
    </div>
  </div>
</template>

<script>
  import { ref } from 'vue'
  import useSlider from './use-slider'

  export default {
    name: 'slider',
    props: {
      sliders: {
        type: Array,
        default() {
          return []
        }
      }
    },
    setup() {
      const rootRef = ref(null)
      const { currentPageIndex } = useSlider(rootRef)
      useSlider(rootRef)
      return {
        rootRef,
        currentPageIndex
      }
    }
  }
</script>

<style lang="scss" scoped>...</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

./views/recommend.vue 组件

  • 获取数据,绑定到推荐页面上
<template>
  <div class="recommend">
    <div class="slider-warpper">
      <div class="slider-content">
        <slider v-if="sliders.length" :sliders="sliders"></slider>
      </div>
    </div>
  </div>
</template>

<script>
  import { getRecommend } from '@/service/recommend'
  import Slider from '@/components/base/slider/slider'
  export default {
    name: 'recommend',
    components: {
      Slider
    },
    data() {
      return {
        sliders: []
      }
    },
    async created() {
      const result = await getRecommend()
      // 拿到数据
      this.sliders = result.sliders
    }
  }
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

歌单列表实现 & 滚动组件(可回弹)

效果图如下:

在这里插入图片描述

./components/scroll/use-scroll.js 文件

observe-dom 插件特性:

  • 针对改变频繁的 CSS 属性,增加 debounce
  • 如果改变发生在 scroll 动画过程中,则不会触发 refresh
import BScroll from '@better-scroll/core'
import ObserveDOM from '@better-scroll/observe-dom'
import { onMounted, onUnmounted, ref } from 'vue'

BScroll.use(ObserveDOM)

export default function useScroll(warpperRef, options) {
    const scroll = ref(null)

    onMounted(() => {
        scroll.value = new BScroll(warpperRef.value, {
            observeDOM: true,
            ...options
        })
    })

    onUnmounted(() => {
        scroll.value.destroy()
    })
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

./component/scroll/scroll.vue 组件

  • 滚动组件使用封装的 js 文件
  • 给 setup 传入默认行为
<template>
  <div ref="rootRef">
    <slot></slot>
  </div>
</template>

<script>
  import useScroll from './use-scroll'
  import { ref } from 'vue'

  export default {
    name: 'scroll',
    // 组件的默认行为
    props: {
      click: {
        type: Boolean,
        default: true
      }
    },
    setup(props) {
      const rootRef = ref(null)
      useScroll(rootRef, props)

      return {
        rootRef
      }
    }
  }
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

./views/recommend.vue 组件

<template>
  <div class="recommend">
    <scroll class="recommend-content">
      <div>
        <div class="slider-wrapper">
          <div class="slider-content">
            <slider v-if="sliders.length" :sliders="sliders"></slider>
          </div>
        </div>
        <div class="recommend-list">
          <h1 class="list-title">热门歌单推荐</h1>
          <ul>
            <li
              v-for="item in albums"
              class="item"
              :key="item.id"
            >
              <div class="icon">
                <img width="60" height="60" :src="item.pic">
              </div>
              <div class="text">
                <h2 class="name">
                  {{ item.username }}
                </h2>
                <p class="title">
                  {{item.title}}
                </p>
              </div>
            </li>
          </ul>
        </div>
      </div>
    </scroll>
  </div>
</template>

<script>
  import { getRecommend } from '@/service/recommend'
  import Slider from '@/components/base/slider/slider'
  import Scroll from '@/components/base/scroll/scroll'

  export default {
    name: 'recommend',
    components: {
    Slider,
    Scroll
},
    data() {
      return {
        sliders: [],
        albums: []
      }
    },
    async created () {
      const result = await getRecommend()
      this.sliders = result.sliders
      this.albums = result.albums
    }
  }
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

不积跬步无以至千里 不积小流无以成江海

点个关注不迷路,持续更新中…

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/76923
推荐阅读
相关标签
  

闽ICP备14008679号