这个笔记是学习Vue.js的时候做的,视频链接:黑马程序员vue前端基础教程-4个小时带你快速入门vue
这个教程只是基础入门,真的基础。。。如果想实际开发的话,还远远不够,我是学后端的,所以前端我也不太会,但是可以交流一下,在学习过程中可以参考我做的这个笔记,也可以参考别人的,反正希望大家都能学好。这个是我写的第一篇博客,有什么不足的地方,欢迎指正和点赞,后续会写更多的博客。
别人的github上有这个Vue.js入门案例的素材,写完笔记才发现,巨亏。。。点击下面的链接即可。
有需要的也可以看看这个,别人写的比较好的笔记
文章最后有福利,但是还是希望大家能耐心看完这篇文章,然后高抬贵手,点个赞,谢谢~
在学习 Vue 之前,建议先学HTML,CSS,JavaScript,AjAx,对前端基础有一定的了解和掌握再来学Vue,因为Vue是建立在这些基础上的一个JavaScript框架。
在聊 Vue 之前先来了解一下前端的发展历程,我学东西喜欢把前因给弄清楚,才不至于学得一脸懵逼。
静态页面
最初的网页以HTML为主,是纯静态的网页。网页是只读的,信息流只能从服务端到客户端单向流通。开发人员
也只关心页面的样式和内容。
动态页面
1995年,网景工程师Brendan Eich 花了10天时间设计了JavaScript语言。
随着JavaScript的诞生,我们可以操作页面的DOM元素及样式,页面有了一些动态的效果,但是依然是以静态为主。
异步刷新
2005年开始,ajax逐渐被前端开发人员所重视,因为不用刷新整个页面就可以更新页面的数据和渲染效果,实际上就是一个局部刷新的效果,当每次页面上的数据发生变化,只需要重新渲染数据发生变化的区域即可,不用刷新整个页面,大大缩短了响应时间,提高了用户体验。
此时的开发人员不仅仅要编写HTML样式,还要懂ajax与后端交互,然后通过JS操作DOM元素来实现页面动态效果。比较流行的框架如 jQuery 就是典型代表。
到这里,基本的前端编写是没有问题了,也可以从后台获取数据模型,
通过JS操作页面的DOM元素,将从后台获取到的数据模型,渲染到页面中了,
同时结合AjAx实现了页面的局部刷新。
但也存在一些问题,如下:
开发人员从后端获取需要的数据模型,然后要通过DOM操作Model,将数据渲染到View中。
而后当用户操作视图,我们还需要通过DOM获取View中的数据,然后同步到Model中。
开发人员仍然要面对很繁琐的DOM操作,这就是存在的问题。
刚刚在上面我们发现了一些存在的问题,为了解决上面的这些问题,提出了 MVVM 模式。
MVVM模式
MVVM模式采用了数据双向绑定机制。也就是说,View的变动,会自动反映在ViewModel上,反之亦然。这样,开发者就不用手动侦听事件并触发相应的View的更新了,因为这些都由MVVM中的VM搞定了。
MVVM中的VM要做的事情就是把DOM操作完全封装起来,开发人员不用再关心Model和View之间是如何互相影响的:
1、只要Model发生了改变,View上自然就会表现出来。
2、当用户修改了View,Model中的数据也会跟着改变。
备注:Vue就是MVVM模式的一种实现框架。
关于MVVM模式可以参考下面的链接:
维基百科(需要)
浅析前端开发中的 MVC/MVP/MVVM 模式
秒懂MVVM模式在Android中的应用
Vue 是一套用于构建用户界面的渐进式JavaScript框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
前端框架三巨头:Vue.js、React.js、AngularJS,vue.js以其轻量易用著称,vue.js和React.js发展速度最快。
特点:
渐进式的JavaScript框架
简化DOM操作
响应式数据驱动
渐进式:
可以选择性的使用该框架的一个或一些组件,这些组件的使用也不需要将框架全部组件都应用;
而且用了这些组件也不要求你的系统全部都使用该框架。
Vue.js官网
步骤:
新建一个HTML文件,编写简单的html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue-HelloWorldtitle>
head>
<body>
<div id="app">
div>
body>
html>
打开官网,导入开发版本的Vue.js(要在使用Vue之前引入)。
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
创建Vue实例对象,设置
el
和data
属性。
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
script>
使用简洁的(差值表达式)模板语法
{{}}
把数据渲染到页面上。
<div id="app">
{{message}}
div>
完整代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue-HelloWorldtitle>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
{{ message }}
div>
<script>
var app = new Vue({
/*el:通过该属性和div进行绑定,div的id属性值和该属性值相同,
也可以是其他选择器,但推荐使用id选择器*/
el: '#app',
/*data:放置要渲染的数据*/
data: {
/*message:要和{{ }}中的字符串一样*/
message: 'Hello Vue!'
}
})
script>
body>
html>
{{ }}:差值表达式,和下面学到的
v-text
指令差不多。
理解成Vue的一种语法即可,用于把数据渲染到页面中。
el:Vue程序挂载点
el
的作用是什么?
el是用来设置Vue实例挂载(管理)的元素。
Vue实例的作用范围是什么?
Vue会管理el属性对应的元素和它内部的后代元素。
是否可以使用其他选择器?
可以使用其他选择器,但是建议使用id选择器,
因为id选择器一般是唯一的。
是否可以设置其他的DOM元素?
Vue可以设置在双标签中,但不支持HTML和 body 标签,
也不支持设置在单标签中,因为单标签之间不能写内容。
data:Vue的数据对象,用于存放数据的。
代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>data数据对象title>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<p>{{msg}}p>
<p>{{people.name}}p>
<p>{{people.age}}p>
<p>{{hobby[0]}}p>
<p>{{hobby[2]}}p>
<p>{{hobby[5]}}p>
div>
<script>
var app = new Vue({
el: '#app',
data: {
//字符串
msg: '隔壁老王来了',
//对象
people: {
name: '老王',
age: 18
},
//数组
hobby: [
'打篮球', '打游戏',
'看美女', '旅游',
'听音乐', '看电影']
},
methods: {
add: function () {
console.log("add...");
}
}
});
script>
body>
html>
data总结
拓展:methods
1、内容绑定,事件绑定(v-text,v-html,v-on(基础))
作用:
{{}}
不会替换,相当于拼接字符串{{}}
都支持表达式代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-text指令title>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<div>
<h2 v-text="msg">广州h2>
<h2>{{msg}}广州h2>
div>
<div>
<h2 v-text="info+'北京'">广州h2>
<h2>{{info + "广州"}}h2>
div>
div>
<script>
var app = new Vue({
el: '#app',
data: {
msg: '深圳',
info: '666'
}
})
script>
body>
html>
作用:
innerHTML
。如果设置的是文本,则原样显示,如果设置的是html
,则会被解析成对应的网页效果。v-text
,解析html用v-html
。代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-html指令title>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<div>
<p v-html="content">哈哈哈p>
<p v-text="content">哈哈哈p>
div>
<div>
<p v-html="msg">p>
<p v-text="msg">p>
div>
div>
<script>
var app = new Vue({
el: '#app',
data: {
content: "嘿嘿嘿~~",
msg: "百度"
}
})
script>
body>
html>
作用:
methods
中this
来调用data
中的数据,方法中的this指代的是当前Vue实例代码如下:
DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>v-on基础title>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<input type="button" value="v-on指令" v-on:click="doWatchTV"/>
<input type="button" value="v-on指令-@简写" @click="doWatchTV"/>
<input type="button" value="v-on指令-@双击" @dblclick="doWatchTV"/>
<h2 @click="changeFood">{{food}}h2>
div>
<script>
var app = new Vue({
el: '#app',
data: {
food: "西红柿炒蛋"
},
methods: {
doWatchTV: function () {
alert("看电视");
},
changeFood: function () {
/*方法中的this指向的是Vue实例对象*/
this.food += "超好吃的!"
}
}
})
script>
body>
html>
2、显示,条件绑定,属性绑定(v-show,v-if,v-bind)
作用:
display
属性值)。v-show
指令,值为布尔值,true显示,false隐藏。代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-show指令title>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<span v-show="isShow">{{ msg }}span>
<div v-show="age>=18">老王 {{age}} 岁了,可以看到了,哇哈哈哈~~~div>
<button @click="changeShow">改变文字显示状态button>
<button @click="changeAge">年龄累加button>
div>
<script>
var app = new Vue({
el: '#app',
data: {
msg: '老王666',
age: 17,
content: "",
isShow: false
},
methods: {
changeShow: function () {
this.isShow = !this.isShow;
},
changeAge: function () {
this.age++;
}
}
})
script>
body>
html>
作用:
DOM
元素)。DOM
的性能消耗较大,v-if
是直接从DOM中移除元素,因此频繁切换的元素使用v-show
。用法:
v-show
一样的用法,只是本质不一样,把v-show
换成v-if
即可。作用:
用法:
代码如下:
DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>v-bind指令title>
head>
<style>
.demo {
width: 100px;
height: 100px;
margin-top: 12px;
background-color: aliceblue;
}
#active {
border-radius: 50%;
background-color: red;
}
#active2 {
border-radius: 50%;
background-color: green;
}
style>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<div>
<a v-bind:href="aHref">百度a>
div>
<div>
<a :href="aHref">百度一下a>
div>
<div :id="isActive?'active':''" class="demo" @click="changeActive">
div>
<div :id="{active2:isActive}" class="demo" @click="changeActive">
div>
div>
<script>
var app = new Vue({
el: '#app',
data: {
isActive: false,
aHref: "https://www.baidu.com"
},
methods: {
changeActive: function () {
console.log("changeActive...");
this.isActive = !this.isActive;
}
}
})
script>
body>
html>
3、列表循环,表单元素绑定(v-for,v-on(补充),v-model)
作用:
用法:
代码如下:
DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>v-for指令title>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<p>第一种写法p>
<ul>
<li v-for="item in arr">
{{item}}
li>
ul>
<br/>
<p>第二种写法p>
<ul>
<li v-for="(item,index) in arr" :key="index">
{{index+1}}--{{item}}
li>
ul>
<div>
<h3 v-for="(item,index) in people" :key="index" v-bind:title="item.name">
{{index}}-->{{item.name}}
h3>
div>
<div>
<button @click="add">添加数据button>
<button @click="remove">移除数据button>
div>
div>
<script>
var app = new Vue({
el: '#app',
data: {
/*普通数组*/
arr: ["Java", "HTML", "JavaScript", "Vue.js"],
/*对象数组*/
people: [
{name: "老王"},
{name: "小红"},
{name: "小欧"}
]
},
methods: {
add: function () {
this.people.push({name: "小卢"});
},
remove: function () {
/*shift();默认移除最左边的元素*/
this.people.shift();
}
}
})
script>
body>
html>
补充:
.修饰符
可以对事件的触发进行更好的控制。.enter
限制的按键为回车键
,事件修饰符还有很多,参照官网即可。代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-on补充title>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<button @click="doSomething('老王',hobby[0].name)">点击弹窗button>
<input type="text" @keydown.enter="doSomething('小卢',hobby[2].name)"/>
div>
<script>
var app = new Vue({
el: '#app',
data: {
hobby: [{name: '睡觉'}, {name: '打游戏'},
{name: '看美女'}, {name: '旅游'},
{name: '听音乐'}, {name: '看电影'}]
},
methods: {
doSomething: function (p1, p2) {
alert("我叫" + p1 + ",喜欢" + p2);
}
}
})
script>
body>
html>
作用:
data
和表单元素数据的双向绑定,不能绑定非表单的元素。代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-model指令title>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<input type="text" v-model="msg">
<br>
<br>
<button @click="setMsg">修改messagebutton>
<br>
<h3>{{msg}}h3>
div>
<script>
var app = new Vue({
el: '#app',
data: {
msg: 'v-model指令'
},
methods: {
getMsg: function () {
alert(this.msg);
},
setMsg: function () {
this.msg = "老王666";
}
}
})
script>
body>
html>
Axios 是一个基于 Promise 的 HTTP(网络请求) 库,简单讲就是可以发送get、post请求等,功能较单一。
Axios同时也是一个JS库,通过Promise实现XHR封装,其中Promise是控制手段,XHR是实际发送Http请求的客户端。
就像$.ajax是通过callback+XHR实现一样,你也可以造个轮子叫XXX的,都是AJAX技术的一种具体实现。
简单来说: AJAX技术是实现网页的局部数据刷新,你可以通过XHR、Fetch、WebSocket等API实现。
axios的GitHub地址
用法:
代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>axios基本使用title>
head>
<body>
<input type="button" value="axios:get请求" class="get">
<input type="button" value="axios:post请求" class="post">
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
/*
接口1:随机笑话
请求地址:https://autumnfish.cn/api/joke/list
请求方法:get
请求参数:num(笑话条数,数字)
响应内容:随机笑话
*/
document.querySelector(".get").onclick = function () {
// axios.get("https://autumnfish.cn/api/joke/list666?num=6")
axios.get("https://autumnfish.cn/api/joke/list?num=6")
.then(
/*成功*/
function (success) {
console.log(success);
},
/*失败*/
function (error) {
console.log(error);
}
)
};
/*
接口2:用户注册
请求地址:https://autumnfish.cn/api/user/reg
请求方法:post
请求参数:username(用户名,字符串)
响应内容:注册成功或失败
*/
document.querySelector(".post").onclick = function () {
// axios.post("https://autumnfish.cn/api/user/reg333", {username: "axios基本使用"})
axios.post("https://autumnfish.cn/api/user/reg", {username: "axios基本使用"})
.then(
/*成功*/
function (success) {
console.log(success);
},
/*失败*/
function (error) {
console.log(error);
}
)
}
script>
body>
html>
随机获取一条笑话,并通过Vue渲染到页面上。
代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue整合axiostitle>
head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<button @click="getJoke">获取一条笑话button>
<p>{{msg}}p>
div>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
/*
接口:随机获取一条笑话
请求地址:https://autumnfish.cn/api/joke
请求方法:get
请求参数:无
响应内容:随机笑话
*/
var app = new Vue({
el: '#app',
data: {
msg: '一条笑话'
},
methods: {
getJoke: function () {
var that = this;
/*把this这个引用传递给that,因为在axios成功和失败的回调函数无法访问this,
this实际上指向的就是当前Vue的实例,拿到this,然后才能把响应数据渲染到页面上*/
axios.get("https://autumnfish.cn/api/joke")
.then(
/*请求成功的回调*/
function (success) {
that.msg = success.data;
},
/*请求失败的回调*/
function (error) {
that.msg = "请求发生错误,请重新请求!";
}
)
}
}
})
script>
body>
html>
总结
- axios回调函数中的this已经改变,无法访问到data中的数据。
- 把this保存到其他变量中(引用传递),回调函数中直接使用保存的this即可。
this
可以通过 . 调用其他方法和data中的数据。- 网络应用和本地应用最大的区别就是改变了数据来源。
需求:
- 点击加号数字加一,但不能超过10;
- 点击减号数字减一,但不能小于0;
- 否则给出提示。
实现思路:
- 使用Vue指令为按钮绑定点击事件
- 在
data
中定义要显示的数字,并赋初值。使用 {{}} 在页面上显示数字- 当事件触发时,改变页面上的数字,也就是
data
中的数字值。在methods
中定义两个方法,进行处理。
代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计数器title>
head>
<style>
.bon {
margin-left: 8px;
}
style>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<button class="bon" @click="add">+button>
<span>{{num}}span>
<button class="bon" @click="sub">-button>
div>
<script>
var app = new Vue({
el: "#app",
data: {
num: 1
},
methods: {
add: function () {
console.log("add...");
console.log(this.num);
if (this.num < 10) {
this.num++;
} else {
alert("不能超过10");
}
},
sub: function () {
console.log(this.num);
console.log("sub...");
if (this.num > 0) {
this.num--;
} else {
alert("不能小于0");
}
}
}
})
script>
body>
html>
需求:
- 点击上一张显示上一张图片,点击下一张显示下一张图片。
- 当前如果是第一张图片,则不显示上一张的按钮,反之则显示。
- 当前如果是最后一张图片,则不显示下一张的按钮,反之则显示。
实现思路:
- 使用Vue指令
v-on:click
或@click
为按钮绑定点击事件。- 在
data
中定义要显示的图片数组(images),以及图片数组的index
,并赋初值。- 在
methods
中定义当点击上一张和下一张的时执行方法。- 使用
v-bind
或:bind
绑定img
的src
属性,图片的路径从图片数组(images)中动态获取。- 使用
v-show
或v-if
配合索引
来判断和控制按钮是否显示和隐藏。
代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图片切换title>
head>
<style>
.img_div {
margin: 18px auto;
width: 560px;
height: 350px;
}
img {
width: 560px;
height: 350px;
}
style>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<body>
<div id="app">
<div class="img_div">
<img :src="'images/'+images[index]" alt="">
div>
<div>
<button v-show="index>0" @click="forward">上一张button>
<br/>
<button v-show="index" @click="next">下一张button>
div>
div>
<script>
var app = new Vue({
el: '#app',
data: {
index: 0,
/*图片名称数组*/
images: [
"bg01.jpg", "bg02.jpg",
"bg03.jpg", "bg04.jpg",
"bg05.jpg", "bg06.jpg",
"bg07.jpg", "bg08.jpg",
"bg09.jpg", "bg10.jpg"
]
},
methods: {
/*上一张*/
forward: function () {
this.index--;
},
/*下一张*/
next: function () {
this.index++;
}
}
})
script>
body>
html>
需求:
- 实现数据的新增。
- 单条删除和清空数据。
- 列表展示,数据条数统计。
- 当列表无数据时,底部的数据条数和批量删除隐藏。
实现思路:
- 新增:用表单进行输入,配合
v-on
,结合事件修饰符,v-model
等实现。- 单条删除:
v-on
为元素绑定单击事件,单击则删除对应数据,传递对应索引删除数组元素即可。- 清空数据:清空列表数(数组)据即可。
- 列表展示:
v-for
实现。- 数据统计:取数组的长度即可。
- 元素的隐藏和显示:
v-show
或v-if
。
代码如下:
html,
body {
margin: 0;
padding: 0;
}
body {
background: #fff;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #f5f5f5;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
}
:focus {
outline: 0;
}
.hidden {
display: none;
}
#todoapp {
background: #fff;
margin: 180px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: gray;
}
#todoapp h1 {
position: absolute;
top: -160px;
width: 100%;
font-size: 60px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, .8);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
border: 0;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.new-todo {
padding: 16px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
}
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
.toggle-all {
width: 1px;
height: 1px;
border: none; /* Mobile Safari */
opacity: 0;
position: absolute;
right: 100%;
bottom: 100%;
}
.toggle-all + label {
width: 60px;
height: 34px;
font-size: 0;
position: absolute;
top: -52px;
left: -13px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.toggle-all + label:before {
content: "❯";
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
.toggle-all:checked + label:before {
color: #737373;
}
.todo-list {
margin: 0;
padding: 0;
list-style: none;
max-height: 420px;
overflow: auto;
}
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
height: 60px;
box-sizing: border-box;
}
.todo-list li:last-child {
border-bottom: none;
}
.todo-list .view .index {
position: absolute;
color: gray;
left: 10px;
top: 20px;
font-size: 16px;
}
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
.todo-list li .toggle {
opacity: 0;
}
.todo-list li .toggle + label {
/*
Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
*/
background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center left;
}
.todo-list li .toggle:checked + label {
background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E");
}
.todo-list li label {
word-break: break-all;
padding: 15px 15px 15px 60px;
display: block;
line-height: 1.2;
transition: color 0.4s;
}
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
margin-bottom: 11px;
transition: color 0.2s ease-out;
}
.todo-list li .destroy:hover {
color: #af5b5e;
}
.todo-list li .destroy:after {
content: "×";
}
.todo-list li:hover .destroy {
display: block;
}
.todo-list li .edit {
display: none;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
.footer {
color: #777;
padding: 10px 15px;
height: 20px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
.footer:before {
content: "";
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.todo-count strong {
font-weight: 300;
}
.filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
}
.clear-completed:hover {
text-decoration: underline;
}
.info {
margin: 50px auto 0;
color: #bfbfbf;
font-size: 15px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
.info p {
line-height: 1;
}
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio: 0) {
.toggle-all,
.todo-list li .toggle {
background: none;
}
.todo-list li .toggle {
height: 40px;
}
}
@media (max-width: 430px) {
.footer {
height: 50px;
}
.filters {
bottom: 10px;
}
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>记事本title>
<link rel="stylesheet" type="text/css" href="./css/index.css"/>
head>
<body>
<section id="todoapp">
<header class="header">
<h1>记事本h1>
<input autofocus="autofocus" autocomplete="off"
placeholder="请输入任务" class="new-todo"
v-model="inputValue" @keyup.enter="add"/>
header>
<section class="main">
<ul class="todo-list">
<li class="todo" v-for="(item,index) in list" :key="index">
<div class="view">
<span class="index">{{index+1}}span>
<label>{{item}}label>
<button class="destroy" @click="remove(index,1)">button>
div>
li>
ul>
section>
<footer class="footer">
<span class="todo-count" v-show="list.length!=0">
共有
<strong>{{list.length}}strong>
项
span>
<button class="clear-completed" @click="clear" v-if="list.length!=0">
清空
button>
footer>
section>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
new Vue({
el: "#todoapp",
data: {
/*数据列表*/
list: ["吃饭", "睡觉", "打豆豆"],
/*文本框输入的值*/
inputValue: ""
},
methods: {
/*新增*/
add: function () {
if (this.inputValue == "") {
alert("输入不能为空!");
return;
}
this.list.push(this.inputValue);
this.inputValue = "";
},
/*移除单个元素*/
remove: function (index, count) {
/*从index位置开始移除,移除count个元素*/
this.list.splice(index, count);
},
/*清空数组*/
clear: function () {
this.list = [];
}
}
})
script>
body>
html>
需求:
- 根据对应的城市,查询对应的天气,并展示到页面上。
实现思路:
v-mode
l实现表单数据和data
的绑定,从而获取输入的城市名。v-on
结合事件修饰符实现回车查询。- 利用
axios
请求天气接口,获取数据。v-for
实现数据的渲染。
代码如下:
/*
请求地址:http://wthrcdn.etouch.cn/weather_mini
请求方法:get
请求参数:city(城市名)
响应内容:天气信息
1. 点击回车
2. 查询数据
3. 渲染数据
*/
new Vue({
el: "#app",
data: {
city: "",
weatherList: []
},
methods: {
getWeather: function () {
if (typeof this.city == "undefined" || this.city == null || this.city == "") {
alert("城市不能为空!");
return;
}
let that = this;
axios.get("http://wthrcdn.etouch.cn/weather_mini?city=" + this.city)
.then(
/*请求成功的函数回调*/
function (success) {
console.log("请求成功!");
console.log(success.data.data);
that.weatherList = success.data.data.forecast;
}
/*,function (error) {
/!*请求失败的函数回调*!/
alert("请求发生错误,请重新请求!");
}*/
).catch(function (error) {
/*请求失败的函数回调*/
alert("请求发生错误,请重新请求!");
})
},
queryWeather: function (city) {
this.city = city;
this.getWeather();
}
}
});
body {
font-family: 'Microsoft YaHei';
}
.wrap {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
/* background: radial-gradient(#f3fbfe, #e4f5fd, #8fd5f4); */
/* background:#8fd5f4; */
/* background: linear-gradient(#6bc6ee, #fff); */
background: #fff;
}
.search_form {
width: 640px;
margin: 100px auto 0;
}
.logo img {
display: block;
margin: 0 auto;
}
.form_group {
width: 640px;
height: 40px;
margin-top: 45px;
}
.input_txt {
width: 538px;
height: 38px;
padding: 0px;
float: left;
border: 1px solid #41a1cb;
outline: none;
text-indent: 10px;
}
.input_sub {
width: 100px;
height: 40px;
border: 0px;
float: left;
background-color: #41a1cb;
color: #fff;
font-size: 16px;
outline: none;
cursor: pointer;
position: relative;
}
.input_sub.loading::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: url('../img/loading.gif');
}
.hotkey {
margin: 8px 0 0 2px;
}
.hotkey a {
font-size: 14px;
color: #666;
padding-right: 15px;
}
.weather_list {
height: 200px;
text-align: center;
margin-top: 50px;
font-size: 0px;
}
.weather_list li {
display: inline-block;
width: 140px;
height: 200px;
padding: 0 10px;
overflow: hidden;
position: relative;
background: url('../img/line.png') right center no-repeat;
background-size: 1px 130px;
}
.weather_list li:last-child {
background: none;
}
/* .weather_list .col02{
background-color: rgba(65, 165, 158, 0.8);
}
.weather_list .col03{
background-color: rgba(94, 194, 237, 0.8);
}
.weather_list .col04{
background-color: rgba(69, 137, 176, 0.8);
}
.weather_list .col05{
background-color: rgba(118, 113, 223, 0.8);
} */
.info_date {
width: 100%;
height: 40px;
line-height: 40px;
color: #999;
font-size: 14px;
left: 0px;
bottom: 0px;
margin-top: 15px;
}
.info_date b {
float: left;
margin-left: 15px;
}
.info_type span {
color: #fda252;
font-size: 30px;
line-height: 80px;
}
.info_temp {
font-size: 14px;
color: #fda252;
}
.info_temp b {
font-size: 13px;
}
.tem .iconfont {
font-size: 50px;
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>天气查询title>
<link rel="stylesheet" href="css/reset.css"/>
<link rel="stylesheet" href="css/index.css"/>
head>
<body>
<div class="wrap" id="app">
<div class="search_form">
<div class="logo">
<img src="img/logo.png" alt="logo"/>
div>
<div class="form_group">
<input type="text" class="input_txt" placeholder="请输入查询的天气"
v-model="city" @keydown.enter="getWeather"/>
<button class="input_sub" @click="getWeather">
搜 索
button>
div>
<div class="hotkey">
<span>热门城市:span>
<a href="javascript:;" @click="queryWeather('北京')">北京a>
<a href="javascript:;" @click="queryWeather('上海')">上海a>
<a href="javascript:;" @click="queryWeather('广州')">广州a>
<a href="javascript:;" @click="queryWeather('深圳')">深圳a>
div>
div>
<ul class="weather_list">
<li v-for="item in weatherList">
<div class="info_type">
<span class="iconfont">{{item.type}}span>div>
<div class="info_temp">
<b>{{item.low}}b>
~
<b>{{item.high}}b>
div>
<div class="info_date">
<span>{{item.date}}span>
div>
li>
ul>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script src="./js/main.js">script>
body>
html>
需求:
- 歌曲的搜索,播放/暂停。
- 歌曲封面随播放歌曲的不同而不同。
- 歌曲的评论。
- 歌曲封面的碟片随歌曲播放而转动(动画)。
- MV的播放/暂停,以及显示。点击MV空白处关闭MV。
- 自动播放下一首歌,若当前歌曲是最后一首,则从头开始播放(列表循环)。
实现思路:
- 歌曲搜索
- 按下回车进行查询:
v-on
,.enter
。- 查询数据:
axios
调用音乐接口,v-model
将输入内容和data
进行绑定。- 渲染数据:
v-for
循环遍历音乐数据即可。- 歌曲播放
- 点击播放:
v-on
(@),绑定点击事件。- 歌曲地址获取:
axios
请求歌曲URL获取接口即可。- 歌曲地址设置:
v-bind:属性名=属性值
(:属性名=属性值),设置src属性的值即可。- 歌曲封面
- 获取封面图片:
axios
请求对应接口获取即可。- 设置封面图片:
v-bind:属性名=属性值
(:属性名=属性值),设置图片的src
属性的值即可。- 播放动画
- 监听音乐的播放和暂停:首先,
audio
标签的play
事件会在音频播放的时候触发,audio
标签的pause
事件会在音频暂停的时候触发。v-on
为音乐的播放(play)和暂停(pause)绑定监听事件,- 同时设置一个标志,播放和暂停时,改变标志的状态即可。
- 配合
v-bind
,播放音乐时为盒子(div)加上类样式修饰即可播放动画,- 即:类样式是否生效,取决于我们所设置的标志,值为true则类样式生效。
- 歌曲MV相关功能
- 歌曲MV图标的显示和隐藏:根据歌曲是否有MV,结合
v-if
或v-show
实现即可。- MV的获取:
axios
请求MV获取接口即可。- 遮罩层显示和隐藏:
v-show
或v-if
,配合v-on
实现。- MV地址切换:
v-bind
设置对应标签的src
属性值即可。- 自动播放下一首(列表循环)
- 播放当前歌曲时,把当前歌曲的索引记录起来。
- 为
audio
标签添加播放结束的监听,若当前歌曲播放结束,则拿到刚刚所记录的歌曲索引,- 判断歌曲索引是否是最后一个,若不是,则索引值加一,调用播放歌曲的方法,进行播放下一首,
- 如果当前歌曲是最后一首,则把索引置为 0,调用播放歌曲的方法,进行歌曲的播放。
- 该功能使用原生JavaScript实现。
代码如下:
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;
}
/*
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的地址
*/
/**
* 判断对象是否为空
* @param obj 要判空的对象
* @returns {boolean} 为空返回true,不为空返回false
*/
function isEmpty(obj) {
if (typeof obj == "undefined" || obj == null || obj == "") {
return true;
} else {
return false;
}
}
var app = new Vue({
el: "#player",
data: {
/*要查询的关键字*/
queryKey: "",
/*歌曲数据列表*/
musicList: [],
/*歌曲url*/
musicUrl: "",
/*当前播放歌曲的索引*/
currentIndex: 0,
/*歌曲封面图片url*/
musicPicUrl: "",
/*歌曲热门评论*/
hotCommentList: [],
/*是否播放动画,false为不播放*/
isPlaying: false,
/*歌曲MV路径*/
songMvUrl: "",
/*MV遮罩层的显示状态,false为不显示*/
isShow: false,
},
methods: {
/*搜索歌曲*/
searchMusic: function () {
if (isEmpty(this.queryKey)) {
alert("搜索关键字不能为空!");
return;
}
let that = this;
axios.get("https://autumnfish.cn/search?keywords=" + this.queryKey)
.then(
/*请求成功的回调*/
function (success) {
console.log(success.data.result.songs);
that.musicList = success.data.result.songs;
}
/*请求失败的回调*/
).catch(function (error) {
alert("请求发生错误,请重新请求!");
return;
})
},
/*播放音乐*/
playMusic: function (musicId, currentIndex) {
/*记录当前播放歌曲索引*/
this.currentIndex = currentIndex;
let that = this;
/*歌曲url获取*/
axios.get("https://autumnfish.cn/song/url?id=" + musicId)
.then(
/*请求成功的回调*/
function (success) {
// console.log(success.data.data[0].url);
that.musicUrl = success.data.data[0].url;
}
/*请求失败的回调*/
).catch(function (error) {
alert("请求发生错误,请重新请求!");
return;
});
/*歌曲详情获取(包含歌曲图片等信息)*/
axios.get("https://autumnfish.cn/song/detail?ids=" + musicId)
.then(
/*请求成功的回调*/
function (success) {
that.musicPicUrl = success.data.songs[0].al.picUrl;
}
/*请求失败的回调*/
).catch(function (error) {
alert("请求发生错误,请重新请求!");
return;
});
/*获取歌曲热门评论*/
axios.get("https://autumnfish.cn/comment/hot?type=0&id=" + musicId)
.then(
/*请求成功的回调*/
function (success) {
// console.log(success);
// console.log(success.data.hotComments);
that.hotCommentList = success.data.hotComments;
}
/*请求失败的回调*/
).catch(function (error) {
alert("请求发生错误,请重新请求!");
return;
});
},
/*动画播放*/
animationPlay: function () {
// console.log("播放...");
this.isPlaying = true;
},
/*动画暂停*/
animationPause: function () {
// console.log("暂停...");
this.isPlaying = false;
},
/*获取歌曲的MV*/
playSongMV: function (mvId) {
// alert(mvId);
let that = this;
axios.get("https://autumnfish.cn/mv/url?id=" + mvId)
.then(
/*请求成功的回调*/
function (success) {
// console.log(success);
that.songMvUrl = success.data.data.url;
/*显示MV遮罩层*/
that.isShow = true;
// console.log("调用成功,url:" + that.songMvUrl)
},
/*请求失败的回调*/
function (error) {
alert("请求发生错误,请重新请求!");
return;
}
)
},
/*隐藏MV遮罩层,并停止MV*/
hideMv: function () {
if (confirm("你确定要关闭当前播放的MV视频吗?")) {
this.isShow = false;
this.songMvUrl = "";
} else {
return;
}
}
},
/*Vue初始化加载完成,执行*/
mounted: function () {
this.queryKey = "胡歌";
this.searchMusic();
}
});
/*自动播放下一首歌曲,原生js实现*/
let audio = document.getElementById("audioPlay");
audio.loop = false;
/*为‘audio’标签添加播放结束事件*/
audio.addEventListener('ended', function () {
// alert("歌曲播放结束!");
// console.log(app.musicList);
let list = app.musicList;
let index = app.currentIndex;
if (index < list.length - 1) {
/*如果不是最后一首*/
let nextMusic = list[index + 1];
app.playMusic(nextMusic.id, index + 1);
} else {
/*如果是最后一首*/
app.playMusic(list[0].id, 0);
}
}, false);
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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" placeholder="输入歌名或歌手"
v-model="queryKey" @keydown.enter="searchMusic"/>
div>
<div class="center_con">
<div class='song_wrapper'>
<ul class="song_list">
<li v-for="(song,index) in musicList" :key="index">
<a href="javascript:;" @click="playMusic(song.id,index)">a>
<b>{{song.name}}b>
<span><i v-if="song.mvid!=0" @click="playSongMV(song.mvid)">i>span>
li>
ul>
<img src="images/line.png" class="switch_btn" alt="">
div>
<div class="player_con" :class="{playing:isPlaying}">
<img src="images/player_bar.png" class="play_bar"/>
<img src="images/disc.png" class="disc autoRotate"/>
<img class="cover autoRotate" :src="musicPicUrl"/>
div>
<div class="comment_wrapper">
<h5 class='title'>热门留言h5>
<div class='comment_list'>
<dl v-for="(comment,index) in hotCommentList" :key="index">
<dt><img :src="comment.user.avatarUrl" alt="">dt>
<dd class="name">{{comment.user.nickname}}dd>
<dd class="detail">{{comment.content}}dd>
dl>
div>
<img src="images/line.png" class="right_line">
div>
div>
<div class="audio_con">
<audio ref='audio' controls autoplay class="myaudio" id="audioPlay"
:src="musicUrl" @play="animationPlay" @pause="animationPause">
audio>
div>
<div class="video_con" style="display: none;" v-show="isShow">
<video controls="controls" autoplay :src="songMvUrl">video>
<div class="mask" @click="hideMv">div>
div>
div>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script src="./js/main.js">script>
body>
html>
总结
网上一个大佬写的Vue.js总结(图片)
学习Vue.js的一个网站
如果想深入学习Vue.js,可以看看官方文档:Vue.js官方文档
也可以看看这个视频教程:2021最新Vue迅速上手教程丨vue3.0入门到精通
上面这些是个人觉得比较好的教程资料,可以参考一下,没有打广告的意思,只是想给大家一个参考。不喜勿喷,谢谢。
最后希望大家看完都能有所收获,本人学后端的,写前端的东西难免有说错的地方,文章如有说错的地方,欢迎批评指正。
后面还会继续写博客的。如果这篇文章对你有所帮助,请高抬贵手,点个赞。谢谢。