1. 网易云音乐的本地服务
通过代理的形式发起请求,中转给前端服务
- 先克隆仓库
- 安装依赖包- 有的公司喜欢搞自己的镜像库 npm cnpm ynpm ccpm bbpm ddpm
- 启动服务-先看package.json的scripts- 运行命令-打包命令-测试命令
运行命令最有可能的几个值 serve-dev-start
3. 初始化项目
- 创建项目 选路由
- 安装axios 和vant ```bash $ npm i axios vant@latest-v2 -S
3. 安装按需引入的插件 babel-plugin-import
```bash
$ npm i babel-plugin-import -D
- 配置按需打包
在main.js中引入reset.css和flexble.js文件plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
4. 创建布局-首页-搜索-播放四个组件
- 路由组件应该建立在views中
- Layout/index.vue
- Home/index.vue
- Search/index.vue
- Play/index.vue
Layout/index.vue
<template>
<div>布局页面</div>
</template>
<script>
export default {
name: 'LayoutPage'
}
</script>
<style scoped>
.main {
padding-top: 46px;
padding-bottom: 50px;
}
</style>
Home/index.vue
<template>
<div>
首页
</div>
</template>
<script>
export default {
name: 'HomePage'
}
</script>
<style scoped>
/* 标题 */
.title {
padding: 0.266667rem 0.24rem;
margin: 0 0 0.24rem 0;
background-color: #eee;
color: #333;
font-size: 15px;
}
/* 推荐歌单 - 歌名 */
.song_name {
font-size: 0.346667rem;
padding: 0 0.08rem;
margin-bottom: 0.266667rem;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box; /** 对象作为伸缩盒子模型显示 **/
-webkit-box-orient: vertical; /** 设置或检索伸缩盒对象的子元素的排列方式 **/
-webkit-line-clamp: 2; /** 显示的行数 **/
overflow: hidden; /** 隐藏超出的内容 **/
}
</style>
Search/index.vue
<template>
<div>搜索页面</div>
</template>
<script>
export default {
name: 'SearchPage'
}
</script>
<style scoped>
/* 搜索容器的样式 */
.search_wrap {
padding: 0.266667rem;
}
/*热门搜索文字标题样式 */
.hot_title {
font-size: 0.32rem;
color: #666;
}
/* 热搜词_容器 */
.hot_name_wrap {
margin: 0.266667rem 0;
}
/* 热搜词_样式 */
.hot_item {
display: inline-block;
height: 0.853333rem;
margin-right: 0.213333rem;
margin-bottom: 0.213333rem;
padding: 0 0.373333rem;
font-size: 0.373333rem;
line-height: 0.853333rem;
color: #333;
border-color: #d3d4da;
border-radius: 0.853333rem;
border: 1px solid #d3d4da;
}
</style>
Play/index.vue-从项目中拷贝
5.路由系统的配置
-layout<br /> - home<br /> - search<br /> - play
并不是所有文件都按需加载
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/views/Layout'
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/layout'
},
{
path: '/layout',
component: Layout,
redirect: '/layout/home', //一级路由和二级路由一起显示
children: [{
// path: '/layout/home' 完整写法
path: 'home',
component: () => import('@/views/Home')
}, {
// path: '/layout/home' 完整写法
path: 'search',
component: () => import('@/views/Search')
}]
},
{
path: '/play',
component: null
}
]
const router = new VueRouter({
routes
})
export default router
layout中需要放置二级路由的容器
<template>
<div>
<router-view></router-view>
<h1>布局页面</h1>
</div>
</template>
6. 底部导航tabbar组件
- 查文档-拷贝示例-main.js ```javascript import { Tabbar, TabbarItem } from ‘vant’;
Vue.use(Tabbar); Vue.use(TabbarItem);
layout/index.vue
```html
<template>
<div>
<router-view></router-view>
<van-tabbar route>
<van-tabbar-item replace to="/layout/home" icon="home-o">首页</van-tabbar-item>
<van-tabbar-item replace to="/layout/search" icon="search">搜索</van-tabbar-item>
</van-tabbar>
</div>
</template>
7. 使用Navbar作为显示标题-动态切换
- 使用vant的Navbar ```javascript import { NavBar } from ‘vant’
Vue.use(NavBar)
2. 在路由中定义meta属性
```javascript
{
path: '/layout',
component: Layout,
redirect: '/layout/home', //一级路由和二级路由一起显示
children: [{
// path: '/layout/home' 完整写法
path: 'home',
component: () => import('@/views/Home'),
meta: {
// 路由提供我们放置自定义属性的
title: "首页"
}
}, {
// path: '/layout/home' 完整写法
path: 'search',
component: () => import('@/views/Search'),
meta: {
title: '搜索'
}
}]
},
- 绑定layout/index.vue 中navbar的title属性
```vue
<a name="bKSLV"></a>
## 8.封装统一的请求-建立独立模块
> axios原生的请求
1. 封装请求工具 utils/request.js
```javascript
import axios from 'axios'
// axios.defaults.baseURL = ""
const instance = axios.create({
baseURL: 'http://localhost:3000', // 基础地址
timeout: 5000 //超时时间
})
// 请求发起之前触发-统一处理参数
// instance.interceptors.request.use(() => {}, () => {}) // 请求拦截器
// 响应数据之后触发- axios默认加了一层data
// instance.interceptors.response.use(() => {}, () => {}) // 响应拦截器
export default instance
- 新建请求模块 api/home.js
- 请求推荐歌单
- 搜索数据 ```javascript import request from ‘@/utils/request’
// 获取推荐歌单 // 网易云的nodejs接口都是get类型 export function recommandMusic (params) { return request({ params, // 地址 url: ‘/personalized’ }) }
v-for="item in 数组"<br />v-for="item in 100"
<a name="e5ZXI"></a>
## 9.实现推荐歌单的渲染
1. 实现静态结构-row-col-image
```vue
<template>
<div>
<p class="title">推荐歌单</p>
<van-row gutter="6">
<van-col span="8" v-for="obj in reList" :key="obj.id">
<van-image width="100%" height="3rem" fit="cover" :src="obj.picUrl" />
<p class="song_name">{{ obj.name }}</p>
</van-col>
</van-row>
</div>
</template>
<script>
export default {
name: 'HomePage',
data() {
return {
reList: []
}
}
}
</script>
不要忘记注册使用的组件
es6+
- 异步编程的终结解决方案 async/await
- async/await必须配套使用
写了await必须父级函数声明async
await 强制等待 await Promise()
- await会强制等待后面的promise进行resolve
让我们写同步那样去写异步
- await会强制等待后面的promise进行resolve
Home/index.vue
<script>
import { recommandMusic } from '@/api/home'
export default {
name: 'HomePage',
data() {
return {
reList: []
}
},
created() {
this.getRecommandMusic()
},
methods: {
// 获取推荐歌单的方法
async getRecommandMusic() {
const { data: { result } } = await recommandMusic({ limit: 6 }) // await会等到它执行成功
// 这里一定是执行成功
//result 等价于 recommandMusic({ limit: 6 }).then(result)
this.reList = result
}
}
}
</script>
10.获取最新歌曲
铺设页面结构
<p class="title">最新音乐</p> <van-cell center v-for="item in 10" :key="item" title="标题" label="描述信息"> <!-- 具名插槽 --> <template #right-icon> <van-icon name="play-circle-o" size="0.6rem" /> </template> </van-cell>
封装获取数据的请求
// 获取最新歌曲的列表 export function getNewSongList (params) { return request({ params, // 地址 url: '/personalized/newsong' }) }
3.调用赋值 ```javascript import { recommandMusic, getNewSongList } from ‘@/api/home’ export default { name: ‘HomePage’, data() { return { reList: [], newList: [] } }, created() { this.getRecommandMusic() this.getNewSongList() }, methods: { // 获取推荐歌单的方法 async getRecommandMusic() { const { data: { result } } = await recommandMusic({ limit: 6 }) // await会等到它执行成功 // 这里一定是执行成功 //result 等价于 recommandMusic({ limit: 6 }).then(result) this.reList = result }, async getNewSongList() { const { data: { result } } = await getNewSongList({ limit: 20 }) this.newList = result } }
}
<a name="Zn8qr"></a>
## 11. 搜索关键词的铺设
1. 先写静态结构
```vue
<template>
<div>
<van-search shape="round" placeholder="请输入搜索关键字"></van-search>
<div class="search_wrap">
<p class="hot_title">热搜关键词</p>
<div class="hot_name_wrap">
<span v-for="item in 100" :key="item" class="hot_item">周杰伦</span>
</div>
</div>
</div>
</template>
- 封装接口 ```javascript import request from ‘@/utils/request’
export function getKeysWordResult(params) { return request({ params, url: ‘/search/hot’ }) }
3. 初始化的时候调用数据
```javascript
import { getKeysWordResult } from '@/api/search'
export default {
name: 'SearchPage',
data() {
return {
keyList: []
}
},
created() {
this.getKeysWordResult()
},
methods: {
async getKeysWordResult() {
const { data: { result: { hots } } } = await getKeysWordResult()
this.keyList = hots
}
}
}
12. 搜索关键字搜索内容
点击关键字将点击内容放入到搜索框里面
<van-search v-model="keyWord" shape="round" placeholder="请输入搜索关键字"></van-search> <div class="search_wrap" v-if="!searchList.length"> <p class="hot_title">热搜关键词</p> <div class="hot_name_wrap"> <span @click="keyWord = item.first" v-for="(item, i) in keyList" :key="i" class="hot_item"> {{ item.first }} </span> </div> </div>
封装了获取歌曲的接口
export function getSearchResult(params) { return request({ params, url: '/cloudsearch' }) }
监听关键字的变化-获取数据-赋值给变量-渲染
watch: { keyWord(newValue) { // 要去搜索 this.getSearchResult(newValue) } },
methods方法
async getSearchResult(keywords) { const { data: { result: { songs } } } = await getSearchResult({ keywords, limit: 20 }) this.searchList = songs }
- 完整代码
```vue
热搜关键词
{{ item.first }}最佳匹配
<a name="d3XRV"></a>
## 13.加载更多
1. 加载更多的需求是手机端的需求,手势往上拉,当列表滚动到底部一定距离的时候,
触发加载下一页的数据
> vant- antd都有触底事件-加载下一页的数据
- 完整代码
```vue
<template>
<div>
<van-search v-model="keyWord" shape="round" placeholder="请输入搜索关键字"></van-search>
<div class="search_wrap" v-if="!searchList.length">
<p class="hot_title">热搜关键词</p>
<div class="hot_name_wrap">
<span @click="keyWord = item.first" v-for="(item, i) in keyList" :key="i" class="hot_item">
{{ item.first }}
</span>
</div>
</div>
<div class="search_wrap" v-else>
<p class="hot_title">最佳匹配</p>
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-cell center v-for="item in searchList" :key="item.id" :title="item.name" :label="item.ar[0].name">
<!-- 具名插槽 -->
<template #right-icon>
<van-icon name="play-circle-o" size="0.6rem" />
</template>
</van-cell>
</van-list>
</div>
</div>
</template>
<script>
import { getKeysWordResult, getSearchResult } from '@/api/search'
export default {
name: 'SearchPage',
data() {
return {
keyList: [], // 热词列表
keyWord: '',
searchList: [], // 搜索的结果列表
loading: false, // 初始值必须为false 只有在false的情况下 才会触发加载事件
finished: false,
page: 1 // 页码
// 是不是已经将所有数据加载完毕
}
},
watch: {
keyWord(newValue) {
// 要去搜索
this.getSearchResult(newValue)
}
},
created() {
this.getKeysWordResult()
},
methods: {
async getKeysWordResult() {
const { data: { result: { hots } } } = await getKeysWordResult()
this.keyList = hots
},
async getSearchResult(keywords) {
const { data: { result: { songs } } } = await getSearchResult({ keywords, limit: 20, offset: (this.page - 1) * 20 })
this.searchList.push(...songs)
},
// 虽然触发了onload事件 loading= true
async onLoad() {
this.page++
await this.getSearchResult(this.keyWord)
// 此时认为执行完毕
this.loading = false
}
}
}
</script>
<style scoped>
/* 搜索容器的样式 */
.search_wrap {
padding: 0.266667rem;
}
/*热门搜索文字标题样式 */
.hot_title {
font-size: 0.32rem;
color: #666;
}
/* 热搜词_容器 */
.hot_name_wrap {
margin: 0.266667rem 0;
}
/* 热搜词_样式 */
.hot_item {
display: inline-block;
height: 0.853333rem;
margin-right: 0.213333rem;
margin-bottom: 0.213333rem;
padding: 0 0.373333rem;
font-size: 0.373333rem;
line-height: 0.853333rem;
color: #333;
border-color: #d3d4da;
border-radius: 0.853333rem;
border: 1px solid #d3d4da;
}
</style>