JavaScript基础知识

  1. ES6语法规范(结构赋值、模板字符串、箭头函数)
  2. ES6模块化(默认、分别、统一暴露)
  3. 包管理器(npm、yarn)
  4. 原型、原型链
  5. 数组常用方法
  6. axios
  7. promise

1 Vue 简介

  • 渐进式JavaScript框架
  • 简化Dom操作
  • 响应式数据驱动

2 Vue 特点

2.1 组件化模式

采用组件化模式,提高代码复用率,易于维护。

2.2 声明式编码

使用Vue指令,无需直接操作DOM,提高开发效率。

2.3 虚拟DOM+Diff

数据->虚拟DOM->真实DOM。

通过Diff比较判断虚拟DOM和真实DOM的区别,从而更新真实DOM。

image-20220607105031042

3 第一个 Vue 程序

  • 导入开发版本Vue.js
  • 创建Vue实例对象,设置 eldata 属性
  • 使用简洁的模板语法把数据渲染到页面上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
{{ message }}
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
</script>
</body>
</html>

image-20210823084926166

4 el 挂载点

el(element:元素),设置挂载点。

  • 作用范围:Vue会管理el选项命中的元素及其内部的后代元素。

  • 选择器:可以使用其他选择器,但是建议使用ID选择器。

  • dom元素:可以使用其他的双标签,但不能使用 htmlbody

5 data 数据对象

  • Vue用到的数据定义在data中。
  • data中可以写复杂类型的数据。
  • 渲染复杂的数据类型时,遵循js语法即可。

6 Vue 指令

6.1 本地应用——计数器

6.1.1 本地应用 v-text 指令

设置标签的文本值(textContent)

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<h2 v-text="message+'!'">地址:</h2>
<h2>地址:{{message+"!"}}</h2>
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
message: "深圳市南山区"
}
})
</script>
</body>
</html>

image-20210823093443387

6.1.2 本地应用 v-html 指令

设置标签的innerHTML,内容中有html结构会被解析为标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<h2 v-html="content"></h2>
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
content: "<a href='https://www.baidu.com'>百度</a>"
}
})
</script>
</body>
</html>

image-20210823094646480

6.1.3 本地应用 v-on 指令

为元素绑定事件,事件名不需要写on,指令可以简写为@。

1
2
3
4
5
6
<div>
<input type="button" value="事件绑定" v-on:click="doIt">
<input type="button" value="事件绑定" v-on:mouseenter="doIt">
<input type="button" value="事件绑定" v-on:dblclick="doIt">
<input type="button" value="事件绑定" @dbclick="doIt">
</div>
1
2
3
4
5
6
7
8
var app = new Vue({
el: "#app",
methods:{
doIt:function(){
// 逻辑
}
}
})
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="button" value="v-on指令" @click="doIt">
</div>
<script>
var hello = new Vue({
el: '#app',
methods: {
doIt:function(){
alert("Do It!")
}
}
})
</script>
</body>
</html>

image-20210823101736406

6.1.4 计数器

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="button" value="-" @click="sub">
<span v-text="count"></span>
<input type="button" value="+" @click="add">
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
sub:function(){
if (this.count > 0) {
this.count--;
}else{
alert("不能再减了!")
}
},
add:function(){
if (this.count < 5) {
this.count++;
}else{
alert("不能再加了!")
}
}
}
})
</script>
</body>
</html>

6.2 本地应用——图片切换

6.2.1 本地应用 v-show 指令

根据布尔值切换显示状态(适用于频繁切换的元素)。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="button" value="切换显示状态" @click="toggleShow">
<img src="./Gitee.png" alt="jpg" v-show="isShow">
<img src="./GitHub.png" alt="jpg" v-show="age>=18">
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
isShow: true,
age: 17
},
methods: {
toggleShow:function(){
this.isShow = !this.isShow;
}
}
})
</script>
</body>
</html>

image-20210823170604902

image-20210823170542542

6.2.2 本地应用 v-if 指令

根据表达式真假,切换元素的显示和隐藏(操作dom元素,对性能消耗大)。

本质是操作dom元素来切换显示状态,表达式为真时,元素存在于dom树中,为假时,从dom树中移除。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="button" value="切换显示" @click="toggleShow">
<p v-if="isShow">Hello Vue by v-if</p>
<p v-show="isShow">Hello Vue by v-show</p>
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
isShow: true,
},
methods: {
toggleShow:function(){
this.isShow = !this.isShow;
}
}
})
</script>
</body>
</html>

image-20210823170644164

image-20210823170711956

6.2.3 本地应用 v-bind 指令

设置元素的属性(比如src,title,class)。

v-bind:属性=表达式(v-bind可省略)。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
<style>
.active {
border: 1px solid red;
}
</style>
</head>
<body>
<div id="app">
<img :src="imgSrc" alt="">
<br/>
<img :src="imgSrc" alt="" :title="imgTitle+'!!!'">
<br/>
<img :src="imgSrc" alt="" :class="{active:isActive}" @click="toggleActive">
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
isActive: false,
imgSrc: "./Gitee.png",
imgTitle: "Gitee"
},
methods: {
toggleActive:function(){
this.isActive = !this.isActive;
}
}
})
</script>
</body>
</html>

6.2.4 图片切换

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
<style>
.active {
border: 1px solid red;
}
</style>
</head>
<body>
<div id="app">
<img :src="imgArr[index]" alt="">
<a href="#" @click="prev" v-show="index!=0">上一张</a>
<a href="#" @click="next" v-show="index<imgArr.length-1">下一张</a>
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
isActive: false,
imgArr: ['./Gitee.png', './GitHub.png', './weixin.png', './QQ.png'],
index: 0
},
methods: {
prev:function(){
if (this.index > 0){
this.index--;
}
},
next:function(){
if (this.index < this.imgArr.length-1){
this.index++;
}
}
}
})
</script>
</body>
</html>

image-20210824134421330

image-20210824134439473

image-20210824134459233

6.3 本地应用——小黑记事本

6.3.1 本地应用 v-for 指令

根据数据生成列表结构。

数组经常和 v-for 结合使用。

语法是( item, index) in 数据

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="button" value="添加数据" @click="add">
<input type="button" value="移除数据" @click="remove">
<ul>
<li v-for="(item, index) in arr">
{{index}} {{ item }}
</li>
</ul>
<h3 v-for="item in vegetables" :title="item.name">
{{ item.name }}
</h3>
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
arr: ["北京", "上海", "广州", "深圳"],
vegetables: [
{name: "西红柿"},
{name: "西兰花"}
]
},
methods: {
add:function(){
this.vegetables.push({name: "西葫芦"});
},
remove:function(){
this.vegetables.shift();
}
}
})
</script>
</body>
</html>

6.3.2 本地应用 v-on 补充

传递自定义参数,事件修饰符(.修饰)。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="button" value="点我" @click="doIt(666, 'adt')">
<input type="text" @keyup.enter="saiHi">
</div>
<script>
var hello = new Vue({
el: '#app',
methods: {
doIt:function(p1, p2){
alert(p1+p2);
},
saiHi:function(){
alert("你好!");
}
}
})
</script>
</body>
</html>

6.3.3 本地应用 v-model 指令

获取和设置表单元素的值(双向数据绑定)。

绑定的数据会和表单元素值相关联。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="button" value="设置message" @click="setMessage">
<input type="text" v-model="message" @keyup.enter="getMessage">
<h3>{{ message }}</h3>
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
message: "深圳市南山区"
},
methods: {
getMessage:function(){
alert(this.message);
},
setMessage:function(){
this.message = "广东省";
}
}
})
</script>
</body>
</html>

6.3.4 小黑记事本

6.3.4.1 新增
  1. 生成列表结构(v-for 数组)。
  2. 获取用户输入(v-model)。
  3. 回车,新增数据(v-on,.enter,添加数据)。
6.3.4.2 删除
  1. 数据改变,和数据绑定的元素也会同步改变。
  2. 事件可接受自定义参数。
  3. 点击删除指定内容(v-on,splice).
6.3.4.3 统计
  1. 统计信息个数(v-text length)
  2. 基于数据的开发方式。
6.3.4.4 清空

点击清除所有信息(v-on,清空数组)。

6.3.4.5 隐藏

没有数据时,隐藏元素(v-show,v-if 数组非空)。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="text" v-model="items" placeholder="请输入待办事项" @keyup.enter="addlist">
<ul>
<li v-for="(item,index) in list">
{{ index+1 }} {{item}}
<input type="button" value="X" @click="remove(index)">
</li>
</ul>
<span v-show="list.length!=0">
<strong>
{{ list.length }} 个待办
</strong>
</span>
<input type="button" value="清空待办" v-show="list.length!=0" @click="removeAll">
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
list: ["写代码", "吃饭", "睡觉"],
items: "",
},
methods: {
addlist:function(){
this.list.push(this.items);
},
remove:function(index){
this.list.splice(index, 1);
},
removeAll:function(){
this.list = [];
}
}
})
</script>
</body>
</html>

6.4 网络应用

6.4.1 axios 基本使用

axios是一个功能强大的网络请求库。

  • axios 必须先导入再使用。
  • 使用 getpost 方法即可发送对应的请求。
  • then 方法中的回调函数会在请求成功或失败时触发。
  • 通过回调函数的形参可以获取响应内容,或错误信息。
1
2
3
4
5
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
// get 语法
axios.get(地址?key1=value1&key2=value2).then(function(reponse){},function(err){})
// post 语法
axios.post(地址,{key1:value1,key2:value2}).then(function(reponse){},function(err){})
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<title>Vue</title>
</head>
<body>
<input type="button" value="get请求" class="get">
<input type="button" value="post请求" class="post">
<script>
/*
接口1:随机笑话
请求地址:https://autumnfish.cn/api/joke/list
请求方法:get
请求参数:num(笑话条数)
响应内容:随机笑话
*/
document.querySelector(".get").onclick = function(){
axios.get("https://autumnfish.cn/api/joke/list?num=3").then(function(response) {
console.log(response);
},function(err){
console.log(err);
})
};
/*
接口2:用户注册
请求地址:https://autumnfish.cn/api/user/reg
请求方法:post
请求参数:username(用户名 字符串)
响应内容:注册成功或失败
*/
document.querySelector(".post").onclick = function(){
axios.post("https://autumnfish.cn/api/user/reg", {username: "jack1122654"}).then(function(response) {
console.log(response);
},function(err){
console.log(err);
})
};
</script>
</body>
</html>

6.4.2 axios 加 vue

  • axios 回调函数中的 this 已经改变,无法访问到 data 中数据。
  • this 保存起来,回调函数中直接使用保存的 this 即可。
  • 和本地应用最大的区别就是改变了数据来源。
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="button" value="获取笑话" @click="getJoke">
<p>{{ jokes }}</p>
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
jokes: "笑话",
},
methods: {
/*
接口1:随机笑话
请求地址:https://autumnfish.cn/api/joke
请求方法:get
请求参数:无
响应内容:获取一条随机笑话
*/
getJoke:function(){
var that = this;
axios.get("https://autumnfish.cn/api/joke").then(function(response) {
that.jokes = response.data;
console.log(response.data);
},function(err){
console.log(err);
});
}
}
})
</script>
</body>
</html>

6.4.3 天知道

  • 应用的逻辑代码建议和页面分离,使用单独的 js 文件编写。
  • axios 回调函数中 this 指向改变了,需要额外保存一份。
  • 服务器返回的数据比较复杂时,获取的时候需要注意层级结构。
6.4.3.1 回车查询
  • 按下回车(v-on,keyup.enter)

  • 查询数据(axios 接口,v-model)

  • 渲染数据(v-for 数组 that)

1
2
3
4
5
6
/*  天气查询接口
请求地址:http://wthrcdn.etouch.cn/weather_mini
请求方法:get
请求参数:city(查询的城市名)
响应内容:天气信息
*/
6.4.3.2 点击查询
  • 点击城市(v-on 自定义参数)。
  • 查询数据(this.方法())。
  • 渲染数据。
  • 自定义参数让代码复用性更高。
  • methods 中定义的方法内部,可以通过 this 关键字点出其他的方法。
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<title>Vue</title>
</head>
<body>
<div id="app">
<input type="text" v-model="city" placeholder="请输入要查询的城市" @keyup.enter="getWeather">
<input type="button" value="搜索" @click="getWeather">
<a href="javascript:;" @click="changeCity('北京')">北京</a>
<a href="javascript:;" @click="changeCity('上海')">上海</a>
<a href="javascript:;" @click="changeCity('广州')">广州</a>
<a href="javascript:;" @click="changeCity('深圳')">深圳</a>
<ul>
<li v-for="item in weatherList">{{item.date}} | {{item.fengxiang}} | {{item.high}} | {{item.low}} | {{item.type}}</li>
</ul>
</div>
<script>
var hello = new Vue({
el: '#app',
data: {
city: "",
weatherList: []
},
methods: {
/*
请求地址:http://wthrcdn.etouch.cn/weather_mini
请求方法:get
请求参数:city(查询的城市名)
响应内容:天气信息
*/
getWeather:function(){
var that = this;
axios.get("http://wthrcdn.etouch.cn/weather_mini?city="+this.city).then(function(response) {
that.weather = response.data.data.forecast;
that.weatherList = response.data.data.forecast;
console.log(response.data);
},function(err){
console.log(err);
});
},
changeCity:function(cityName){
this.city = cityName;
this.getWeather();
}
}
})
</script>
</body>
</html>

6.5 综合应用

6.5.1 音乐查询

1
2
3
4
5
6
/*	歌曲搜索接口
请求地址:https://autumnfish.cn/search
请求方法:get
请求参数:keywords(查询的关键字)
响应内容:歌曲搜索结果
*/
  • 按下回车(v-on keyup.enter)。
  • 查询数据(axios接口 v-model)。
  • 渲染数据(v-for 数组 that)。

6.5.2 音乐播放

1
2
3
4
5
6
/*	歌曲url获取接口
请求地址:https://autumnfish.cn/song/url
请求方法:get
请求参数:id(歌曲id)
响应内容:歌曲的url地址
*/
  • 点击播放(v-on)。
  • 歌曲地址获取(接口 歌曲id)。
  • 歌曲地址设置(v-bind)。
  • 歌曲id依赖于歌曲搜索的结果,对于不用的数据也需要关注。

6.5.3 歌曲封面

1
2
3
4
5
6
/*	歌曲详情获取接口
请求地址:https://autumnfish.cn/song/detail
请求方法:get
请求参数:ids(歌曲id)
响应内容:歌曲详情,包含封面信息
*/
  • 点击播放(v-on 增加逻辑)。
  • 歌曲封面获取(接口 歌曲id)。
  • 歌曲封面设置(v-bind)。

6.5.4 歌曲评论

1
2
3
4
5
6
/*	歌曲热门评论获取接口
请求地址:https://autumnfish.cn/comment/hot?type=0
请求方法:get
请求参数:id(歌曲id,type固定为0)
响应内容:歌曲的热门评论
*/
  • 点击播放。
  • 歌曲评论获取。
  • 歌曲评论渲染。

6.5.5 播放动画

  • 监听音乐播放(v-on play)。
  • 监听音乐暂停(v-on pause)。
  • 操纵类名(v-bind 对象)。
  • audio标签的play事件会在音频播放的时候触发。
  • audio标签的pause事件会在音频暂停的时候触发。
  • 通过对象的方式设置类名,类名生效与否取决于后面值得真假。

6.5.6 播放 mv

1
2
3
4
5
6
/*	
请求地址:https://autumnfish.cn/mv/url
请求方法:get
请求参数:id(mvid,为0表示没有mv)
响应内容:mv的地址
*/
6.5.6.1 index.css
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
body,
ul,
dl,
dd {
margin: 0px;
padding: 0px;
}

.wrap {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: url("../images/bg.jpg") no-repeat;
background-size: 100% 100%;
}

.play_wrap {
width: 800px;
height: 544px;
position: fixed;
left: 50%;
top: 50%;
margin-left: -400px;
margin-top: -272px;
/* background-color: #f9f9f9; */
}

.search_bar {
height: 60px;
background-color: #1eacda;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
z-index: 11;
}

.search_bar img {
margin-left: 23px;
}

.search_bar input {
margin-right: 23px;
width: 296px;
height: 34px;
border-radius: 17px;
border: 0px;
background: url("../images/zoom.png") 265px center no-repeat
rgba(255, 255, 255, 0.45);
text-indent: 15px;
outline: none;
}

.center_con {
height: 435px;
background-color: rgba(255, 255, 255, 0.5);
display: flex;
position: relative;
}

.song_wrapper {
width: 200px;
height: 435px;
box-sizing: border-box;
padding: 10px;
list-style: none;
position: absolute;
left: 0px;
top: 0px;
z-index: 1;
}

.song_stretch {
width: 600px;
}

.song_list {
width: 100%;
overflow-y: auto;
overflow-x: hidden;
height: 100%;
}
.song_list::-webkit-scrollbar {
display: none;
}

.song_list li {
font-size: 12px;
color: #333;
height: 40px;
display: flex;
flex-wrap: wrap;
align-items: center;
width: 580px;
padding-left: 10px;
}

.song_list li:nth-child(odd) {
background-color: rgba(240, 240, 240, 0.3);
}

.song_list li a {
display: block;
width: 17px;
height: 17px;
background-image: url("../images/play.png");
background-size: 100%;
margin-right: 5px;
box-sizing: border-box;
}

.song_list li b {
font-weight: normal;
width: 122px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.song_stretch .song_list li b {
width: 200px;
}

.song_stretch .song_list li em {
width: 150px;
}

.song_list li span {
width: 23px;
height: 17px;
margin-right: 50px;
}
.song_list li span i {
display: block;
width: 100%;
height: 100%;
cursor: pointer;
background: url("../images/table.png") left -48px no-repeat;
}

.song_list li em,
.song_list li i {
font-style: normal;
width: 100px;
}

.player_con {
width: 400px;
height: 435px;
position: absolute;
left: 200px;
top: 0px;
}

.player_con2 {
width: 400px;
height: 435px;
position: absolute;
left: 200px;
top: 0px;
}

.player_con2 video {
position: absolute;
left: 20px;
top: 30px;
width: 355px;
height: 265px;
}

.disc {
position: absolute;
left: 73px;
top: 60px;
z-index: 9;
}
.cover {
position: absolute;
left: 125px;
top: 112px;
width: 150px;
height: 150px;
border-radius: 75px;
z-index: 8;
}
.comment_wrapper {
width: 180px;
height: 435px;
list-style: none;
position: absolute;
left: 600px;
top: 0px;
padding: 25px 10px;
}
.comment_wrapper .title {
position: absolute;
top: 0;
margin-top: 10px;
}
.comment_wrapper .comment_list {
overflow: auto;
height: 410px;
}
.comment_wrapper .comment_list::-webkit-scrollbar {
display: none;
}
.comment_wrapper dl {
padding-top: 10px;
padding-left: 55px;
position: relative;
margin-bottom: 20px;
}

.comment_wrapper dt {
position: absolute;
left: 4px;
top: 10px;
}

.comment_wrapper dt img {
width: 40px;
height: 40px;
border-radius: 20px;
}

.comment_wrapper dd {
font-size: 12px;
}

.comment_wrapper .name {
font-weight: bold;
color: #333;
padding-top: 5px;
}

.comment_wrapper .detail {
color: #666;
margin-top: 5px;
line-height: 18px;
}
.audio_con {
height: 50px;
background-color: #f1f3f4;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.myaudio {
width: 800px;
height: 40px;
margin-top: 5px;
outline: none;
background-color: #f1f3f4;
}
/* 旋转的动画 */
@keyframes Rotate {
from {
transform: rotateZ(0);
}
to {
transform: rotateZ(360deg);
}
}
/* 旋转的类名 */
.autoRotate {
animation-name: Rotate;
animation-iteration-count: infinite;
animation-play-state: paused;
animation-timing-function: linear;
animation-duration: 5s;
}
/* 是否正在播放 */
.player_con.playing .disc,
.player_con.playing .cover {
animation-play-state: running;
}

.play_bar {
position: absolute;
left: 200px;
top: -10px;
z-index: 10;
transform: rotate(-25deg);
transform-origin: 12px 12px;
transition: 1s;
}
/* 播放杆 转回去 */
.player_con.playing .play_bar {
transform: rotate(0);
}
/* 搜索历史列表 */
.search_history {
position: absolute;
width: 296px;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.3);
list-style: none;
right: 23px;
top: 50px;
box-sizing: border-box;
padding: 10px 20px;
border-radius: 17px;
}
.search_history li {
line-height: 24px;
font-size: 12px;
cursor: pointer;
}
.switch_btn {
position: absolute;
right: 0;
top: 0;
cursor: pointer;
}
.right_line {
position: absolute;
left: 0;
top: 0;
}
.video_con video {
position: fixed;
width: 800px;
height: 546px;
left: 50%;
top: 50%;
margin-top: -273px;
transform: translateX(-50%);
z-index: 990;
}
.video_con .mask {
position: fixed;
width: 100%;
height: 100%;
left: 0;
top: 0;
z-index: 980;
background-color: rgba(0, 0, 0, 0.8);
}
.video_con .shutoff {
position: fixed;
width: 40px;
height: 40px;
background: url("../images/shutoff.png") no-repeat;
left: 50%;
margin-left: 400px;
margin-top: -273px;
top: 50%;
z-index: 995;
}
6.5.6.2 main.js
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
1 歌曲搜索接口
请求地址:https://autumnfish.cn/search
请求方法:get
请求参数:keywords(查询关键字)
响应内容:歌曲搜索结果
2 歌曲url获取接口
请求地址:https://autumnfish.cn/song/url
请求方法:get
请求参数:id(歌曲id)
响应内容:歌曲url地址
3 歌曲详情获取
请求地址:https://autumnfish.cn/song/detail
请求方法:get
请求参数:ids(歌曲id)
响应内容:歌曲详情(包括封面信息)
4 热门评论获取
请求地址:https://autumnfish.cn/comment/hot?type=0
请求方法:get
请求参数:id(歌曲id,地址中的type固定为0)
响应内容:歌曲的热门评论
5 mv地址获取
请求地址:https://autumnfish.cn/mv/url
请求方法:get
请求参数:id(mvid,为0表示没有mv)
响应内容:mv的地址
*/
var app = new Vue({
el: "#player",
data: {
qurey:"",
musicList:[],
musicUrl:"",
musicDetail:"",
hotComments:[],
// 动画播放
isPlaying:false,
// 遮罩层显示状态
isShow: false,
// mv地址
mvUrl:""
},
methods: {

// 歌曲搜索
searchMusic:function(){
var that = this;
axios.get("https://autumnfish.cn/search?keywords="+this.qurey)
.then(function(response){
that.musicList = response.data.result.songs;
}, function(err){console.log(err);});
},

// 歌曲播放
musicPlayer:function(musicId){
var that = this;

// 获取播放链接
axios.get("https://autumnfish.cn/song/url?id="+musicId)
.then(function(response){
that.musicUrl = response.data.data[0].url;
}, function(err){console.log(err);});

// 获取歌曲封面
axios.get("https://autumnfish.cn/song/detail?ids="+musicId)
.then(function(response){
that.musicDetail = response.data.songs[0].al.picUrl;
},function(err){console.log(err);});

// 获取歌曲热门评论
axios.get("https://autumnfish.cn/comment/hot?type=0&id="+musicId)
.then(function(response){
that.hotComments = response.data.hotComments;
}, function(err){console.log(err);});
},

// 播放状态
play:function(){
this.isPlaying = true;
},

// 暂停状态
pause: function(){
this.isPlaying = false;
},

// 播放mv
playMv:function(mvid){
var that = this;
axios.get("https://autumnfish.cn/mv/url?id="+mvid)
.then(function(response){
that.isShow = true;
that.mvUrl = response.data.data.url;
this.isPlaying = false;
}, function(err){console.log(err);});
},

// 隐藏
hide:function(){
this.isShow = false;
this.mvUrl = "";
}
}
});
6.5.6.3 music.html
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
61
62
63
64
65
66
67
68
69
70
71
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>悦听</title>
<!-- 样式 -->
<link rel="stylesheet" href="./css/index.css">
</head>

<body>
<div class="wrap">
<!-- 播放器主体区域 -->
<div class="play_wrap" id="player">
<div class="search_bar">
<img src="images/player_title.png" alt="" />
<!-- 搜索歌曲 -->
<input type="text" autocomplete="off" v-model="qurey" @keyup.enter="searchMusic" />
</div>
<div class="center_con">
<!-- 搜索歌曲列表 -->
<div class='song_wrapper'>
<ul class="song_list">
<li v-for="item in musicList">
<a href="javascript:;" @click="musicPlayer(item.id)"></a>
<b>{{item.name}}</b>
<span v-if="item.mvid!=0" @click="playMv(item.mvid)"><i></i></span></li>
</li>
</ul>
<img src="images/line.png" class="switch_btn" alt="">
</div>
<!-- 歌曲信息容器 -->
<div class="player_con" :class="{playing:isPlaying}" class="playing">
<img src="images/player_bar.png" class="play_bar" />
<!-- 黑胶碟片 -->
<img src="images/disc.png" class="disc autoRotate" />
<img :src="musicDetail" class="cover autoRotate" />
</div>
<!-- 评论容器 -->
<div class="comment_wrapper">
<h5 class='title'>热门留言</h5>
<div class='comment_list'>
<dl v-for="item in hotComments">
<dt><img :src="item.user.avatarUrl" alt=""></dt>
<dd class="name">{{item.user.nickname}}</dd>
<dd class="detail">
{{item.content}}
</dd>
</dl>
</div>
<img src="images/line.png" class="right_line">
</div>
</div>
<div class="audio_con">
<audio ref='audio' @play="play" @pause="pause" :src="musicUrl" controls autoplay loop class="myaudio"></audio>
</div>
<div class="video_con" v-show="isShow" style="display: none;">
<video :src="mvUrl" controls="controls"></video>
<div class="mask" @click="hide"></div>
</div>
</div>
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 官网提供的 axios 在线地址 -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!-- 自己的js -->
<script src="./js/main.js"></script>
</body>
</html>