文章目錄
- Vue基礎入門
- 1、Vue基礎入門
- 1.1 前置知識
- 1.2 前端的發展(了解)
- 1.3 Vue是什么
- 1.4 第一個Vue程式
- 1.4.1 Vue程式分析
- 2、Vue指令
- v-text
- v-html
- v-on(基礎)
- v-show
- v-if
- v-bind
- v-for
- v-on(補充)
- v-model
- 3、Vue網路應用
- 3.1 什么是Axios
- 3.2 Axios基本使用
- 3.3 Vue整合Axios
- 4、案例
- 4.1 計數器
- 4.2 圖片切換
- 4.3 記事本
- index.css
- 記事本.html
- 4.4 天氣查詢
- main.js
- index.css
- 天氣查詢.html
- 4.5 音樂播放器
- index.css
- main.js
- 音樂播放器.html
- 5、其他資料(福利)
Vue基礎入門
這個筆記是學習Vue.js的時候做的,視頻鏈接:黑馬程式員vue前端基礎教程-4個小時帶你快速入門vue
這個教程只是基礎入門,真的基礎,,,如果想實際開發的話,還遠遠不夠,我是學后端的,所以前端我也不太會,但是可以交流一下,在學習程序中可以參考我做的這個筆記,也可以參考別人的,反正希望大家都能學好,這個是我寫的第一篇博客,有什么不足的地方,歡迎指正和點贊,后續會寫更多的博客,
別人的github上有這個Vue.js入門案例的素材,寫完筆記才發現,巨虧,,,點擊下面的鏈接即可,
有需要的也可以看看這個,別人寫的比較好的筆記
文章最后有福利,但是還是希望大家能耐心看完這篇文章,然后高抬貴手,點個贊,謝謝~
1、Vue基礎入門
1.1 前置知識
在學習 Vue 之前,建議先學HTML,CSS,JavaScript,AjAx,對前端基礎有一定的了解和掌握再來學Vue,因為Vue是建立在這些基礎上的一個JavaScript框架,
1.2 前端的發展(了解)
在聊 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操作,這就是存在的問題,
- 2008年,google的Chrome發布,隨后就以極快的速度占領市場,超過IE成為瀏覽器市場的主導者,
- 2009年,Ryan Dahl在谷歌的Chrome V8引擎基礎上,打造了基于事件回圈的異步IO框架:Node.js,
- 2010年,NPM作為node.js的包管理系統首次發布,開發人員可以遵循Common.js規范來撰寫Node.js模塊,然后發布到NPM上供其他開發人員使用,目前已經是世界最大的包模塊管理系統,
- node.js的偉大之處不在于讓JS邁向了后端開發,而是構建了一個龐大的生態系統,
- 隨后,在node的基礎上,涌現出了一大批的前端框架,Vue 就是其中一個,
剛剛在上面我們發現了一些存在的問題,為了解決上面的這些問題,提出了 MVVM 模式,
MVVM模式
- M:即Model,模型,包括資料和一些基本操作
- V:即View,視圖,頁面渲染結果
- VM:即View-Model,模型與視圖間的雙向操作(無需開發人員干涉)
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中的應用
1.3 Vue是什么
Vue 是一套用于構建用戶界面的漸進式JavaScript框架,與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用,Vue 的核心庫只關注視圖層,不僅易于上手,還便于與第三方庫或既有專案整合,另一方面,當與現代化的工具鏈以及各種支持類別庫結合使用時,Vue 也完全能夠為復雜的單頁應用提供驅動,
前端框架三巨頭:Vue.js、React.js、AngularJS,vue.js以其輕量易用著稱,vue.js和React.js發展速度最快,
特點:
漸進式的JavaScript框架
簡化DOM操作
回應式資料驅動
漸進式:
可以選擇性的使用該框架的一個或一些組件,這些組件的使用也不需要將框架全部組件都應用;
而且用了這些組件也不要求你的系統全部都使用該框架,
1.4 第一個Vue程式
Vue.js官網
步驟:
新建一個HTML檔案,撰寫簡單的html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue-HelloWorld</title>
</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-HelloWorld</title>
</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>
1.4.1 Vue程式分析
{{ }}:差值運算式,和下面學到的
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總結
- data用于定義Vue中用到的資料
- data中可以寫復雜型別的資料
- 渲染復雜型別資料時,遵循JS的語法即可,如:物件的 . 語法,陣列的索引語法,
拓展:methods
- methods中用于定義方法(函式),
2、Vue指令
1、內容系結,事件系結(v-text,v-html,v-on(基礎))
v-text
作用:
- 設定標簽文本的文本值(textContent),無論設定什么內容,只會決議為文本
- v-text會替換標簽內的文本值,
{{}}不會替換,相當于拼接字串 - v-text和
{{}}都支持運算式
代碼如下:
<!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>
v-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>
<!--普通文本和v-text顯示效果一樣-->
<!--‘哈哈哈’會被全部替換-->
<p v-html="content">哈哈哈</p>
<p v-text="content">哈哈哈</p>
</div>
<div>
<!--v-html決議為網頁效果-->
<p v-html="msg"></p>
<!--v-text決議為純文本-->
<p v-text="msg"></p>
</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
content: "嘿嘿嘿~~",
msg: "<a href='https://www.baidu.com'>百度</a>"
}
})
</script>
</body>
</html>
v-on(基礎)
作用:
- 為元素系結事件,如:滑鼠點擊,滑鼠移出,按下鍵盤事件…
- 事件名不用寫on
- 指令可以簡寫為@
- 事件系結的方法(也就是事件觸發時執行的函式)定義在
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)
v-show
作用:
- 根據給定運算式,判斷運算式值的真偽,來控制元素的顯示和隱藏(原理是修改元素的
display屬性值), - 指令后面的內容最終都會決議為布林值,原理是修改元素的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>
v-if
作用:
- 根據運算式的真偽,切換元素的顯示隱藏(原理是操作的是
DOM元素), - 操作
DOM的性能消耗較大,v-if是直接從DOM中移除元素,因此頻繁切換的元素使用v-show,
用法:
- 和
v-show一樣的用法,只是本質不一樣,把v-show換成v-if即可,
v-bind
作用:
- 設定元素的屬性,如a標簽的href屬性,img的src屬性,class類樣式屬性…
用法:
- v-bind:屬性名=運算式
- :屬性名=運算式
代碼如下:
<!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>
<!--方式二:物件方式 active2這個選擇器是否生效,取決于isActive的值,
不知道為什么沒生效...
-->
<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)
v-for
作用:
- 用于生成串列和遍歷資料,
- 可以結合其他指令一起使用,
用法:
- 情況1:v-for=“當前回圈到的元素 in 待回圈的資料” :key=“唯一值”
- 情況2:v-for="(當前回圈到的元素,當前的索引) in 待回圈的資料" :key=“索引”
- vue中,回圈的時候,key屬性加不加不會報錯,只是為了提高頁面的渲染性能,建議加上,不影響顯示效果,
- 如果沒有id,可以考慮使用索引替代,切記key的值不能重復,只要遵循不重復的原則即可,值是什么無所謂,
代碼如下:
<!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>
<!--物件陣列,v-bind:屬性名=屬性值 是進行屬性系結的,可簡寫為 :屬性名=屬性值-->
<h3 v-for="(item,index) in people" :key="index" v-bind:title="item.name">
{{index}}-->{{item.name}}
</h3>
</div>
<!--測驗陣列資料變化對v-for的影響-->
<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>
v-on(補充)
補充:
- 事件傳參
- 定義方法時需要定義形參來接收傳入的實參,
- 事件修飾符:v-on補充-修飾符
- 事件的后面加上
.修飾符可以對事件的觸發進行更好的控制, .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>
<!--事件修飾符:鍵盤按下enter鍵觸發doSomething事件-->
<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>
v-model
作用:
- 獲取和設定表單元素的值(雙向資料系結),
- 只能獲取和設定表單元素的值,實作
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">修改message</button>
<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>
3、Vue網路應用
3.1 什么是Axios
Axios 是一個基于 Promise 的 HTTP(網路請求) 庫,簡單講就是可以發送get、post請求等,功能較單一,
Axios同時也是一個JS庫,通過Promise實作XHR封裝,其中Promise是控制手段,XHR是實際發送Http請求的客戶端,
就像$.ajax是通過callback+XHR實作一樣,你也可以造個輪子叫XXX的,都是AJAX技術的一種具體實作,
簡單來說: AJAX技術是實作網頁的區域資料重繪,你可以通過XHR、Fetch、WebSocket等API實作,
axios的GitHub地址
3.2 Axios基本使用
用法:
- axios必須先匯入才可以使用,
- 使用get或post方法即可發送對應的請求,
- then方法中的回呼函式會在請求成功或失敗時觸發,
- 第一個回呼函式在成功時觸發,
- 第二個回呼函式在失敗時觸發,
- 通過回呼函式的形參可以獲取回應內容或錯誤資訊,
代碼如下:
<!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>
3.3 Vue整合Axios
隨機獲取一條笑話,并通過Vue渲染到頁面上,
代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue整合axios</title>
</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中的資料,- 網路應用和本地應用最大的區別就是改變了資料來源,
4、案例
4.1 計數器
需求:
- 點擊加號數字加一,但不能超過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>
4.2 圖片切換
需求:
- 點擊上一張顯示上一張圖片,點擊下一張顯示下一張圖片,
- 當前如果是第一張圖片,則不顯示上一張的按鈕,反之則顯示,
- 當前如果是最后一張圖片,則不顯示下一張的按鈕,反之則顯示,
實作思路:
- 使用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<images.length-1" @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>
4.3 記事本
需求:
- 實作資料的新增,
- 單條洗掉和清空資料,
- 串列展示,資料條數統計,
- 當串列無資料時,底部的資料條數和批量洗掉隱藏,
實作思路:
- 新增:用表單進行輸入,配合
v-on,結合事件修飾符,v-model等實作,- 單條洗掉:
v-on為元素系結單擊事件,單擊則洗掉對應資料,傳遞對應索引洗掉陣列元素即可,- 清空資料:清空串列數(陣列)據即可,
- 串列展示:
v-for實作,- 資料統計:取陣列的長度即可,
- 元素的隱藏和顯示:
v-show或v-if,
代碼如下:
index.css
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;
}
}
記事本.html
<!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>
<!--v-model:表單資料和data中的inputValue進行雙向系結
@keyup.enter(v-on:keyup.enter):鍵盤上的enter鍵抬起時,觸發(執行)add方法-->
<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>
<!--從index位置開始移除,移除1個元素-->
<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>
4.4 天氣查詢
需求:
- 根據對應的城市,查詢對應的天氣,并展示到頁面上,
實作思路:
v-model實作表單資料和data的系結,從而獲取輸入的城市名,v-on結合事件修飾符實作回車查詢,- 利用
axios請求天氣介面,獲取資料,v-for實作資料的渲染,
代碼如下:
main.js
/*
請求地址: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();
}
}
});
index.css
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;
}
天氣查詢.html
<!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>
<!-- 官網提供的 axios 在線地址 -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!--自定義的js-->
<script src="./js/main.js"></script>
</body>
</html>
4.5 音樂播放器
需求:
- 歌曲的搜索,播放/暫停,
- 歌曲封面隨播放歌曲的不同而不同,
- 歌曲的評論,
- 歌曲封面的碟片隨歌曲播放而轉動(影片),
- 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實作,
代碼如下:
index.css
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;
}
main.js
/*
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;
}
}
}
});
/*自動播放下一首歌曲,原生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);
音樂播放器.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>音樂播放器</title>
<!-- 自定義css -->
<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=""/>
<!-- 搜索歌曲 -->
<!--v-model:對表單輸入框進行系結
@keydown.enter(v-bind:keydown.enter):按下回車鍵搜索-->
<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>
<!--當歌曲的mvid!=0的時候表示有MV,則顯示MV圖示,
因為圖示不需要頻繁的顯示和隱藏,所以用‘v-if’指令-->
<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>
<!-- 歌曲資訊容器 -->
<!--v-bind系結屬性class,‘playing’這個類樣式是否生效,影片是否播放取決于‘isPlaying’的值-->
<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’的src屬性值-->
<!--當點擊播放(@play="animationPlay")和暫停(@pause="animationPause")時,觸發對應的方法改變影片播放的標志-->
<audio ref='audio' controls autoplay class="myaudio" id="audioPlay"
:src="musicUrl" @play="animationPlay" @pause="animationPause">
</audio>
</div>
<!--MV遮罩層的div,默認不顯示-->
<!--設定MV遮罩層并設定MV地址-->
<!--v-if是通過添加和移除dom元素來實作顯示隱藏,v-show是通過改變元素的display屬性值實作顯示隱藏,
style="display: none;"和v-if不能同時使用-->
<div class="video_con" style="display: none;" v-show="isShow">
<video controls="controls" autoplay :src="songMvUrl"></video>
<!--點擊MV外面隱藏(關閉)遮罩層和歌曲MV-->
<div class="mask" @click="hideMv"></div>
</div>
</div>
</div>
<!-- 開發環境版本,包含了有幫助的命令列警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/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>
總結
- 對于本地無法獲取的資料,基本上都會有對應的介面,
5、其他資料(福利)
網上一個大佬寫的Vue.js總結(圖片)
學習Vue.js的一個網站
如果想深入學習Vue.js,可以看看官方檔案:Vue.js官方檔案
也可以看看這個視頻教程:2021最新Vue迅速上手教程丨vue3.0入門到精通
上面這些是個人覺得比較好的教程資料,可以參考一下,沒有打廣告的意思,只是想給大家一個參考,不喜勿噴,謝謝,
最后希望大家看完都能有所識訓,本人學后端的,寫前端的東西難免有說錯的地方,文章如有說錯的地方,歡迎批評指正,
后面還會繼續寫博客的,如果這篇文章對你有所幫助,請高抬貴手,點個贊,謝謝,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/291056.html
標籤:其他
上一篇:JS中使用reduce函式配合css3影片實作超好看的文字效果
下一篇:通過javascript封裝一個getType函式能夠獲取所有不同的資料型別,例如Symbol,null,object等
