vue概述
mvvm模式
-
M:即Model,模型,包括數據和一些基本操作 -
V:即View,視圖,頁面渲染結果 -
VM:即View-Model,模型與視圖間的雙向操作(無需開發人員干涉)
在MVVM之前,開發人員從後端獲取需要的數據模型,然後要通過DOM操作Model渲染到View中。而後當用戶操作視圖,我們還需要通過DOM獲取View中的數據,然後同步到Model中。
而MVVM中的VM要做的事情就是把DOM操作完全封裝起來,開發人員不用再關心Model和View之間是如何互相影響的:
-
只要我們Model發生了改變,View上自然就會表現出來。 -
當用戶修改了View,Model中的數據也會跟着改變。
把開發人員從繁瑣的DOM操作中解放出來,把關注點放在如何操作Model上。
mvvm模式的優勢:
-
低耦合
視圖(View)可以獨立於Model變化和修改,一個ViewModel可以綁定到不同的"View"上,當View變化時Model可以不變,當Model變化時View也可以不變
-
可重用性
可以把一些視圖邏輯放在一個ViewModel裏面,讓多個View重用這段視圖邏輯代碼
-
獨立開發
開發人員可以專注於業務邏輯和數據的開發(ViewModel),設計人員可以專注於頁面設計。
而我們今天要學習的,就是一款MVVM模式的框架:Vue
快速入門
安裝vue
下載安裝
下載地址:https://github.com/vuejs/vue
可以下載2.5.16版本https://github.com/vuejs/vue/archive/v2.5.16.zip
下載解壓,得到vue.js文件。可以在頁面中直接通過script引入vue.js文件
使用cdn
或者也可以直接使用公共的CDN服務:
<!-- 開發環境版本,包含了用幫助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
或者:
<!-- 生產環境版本,優化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
Vue 入門案例(演示用,不做講解)
HTML模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>{{name}},非常酷!</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el: "#app", // el即element,要渲染的頁面元素
data: {
name: "Vue測試"
}
});
</script>
</body>
</html>
-
首先通過 new Vue()
來創建Vue
實例 -
然後構造函數接收一個對象,對象中有一些屬性: -
name
:這裏指定了一個name
屬性 -
el :是 element
的縮寫,通過id
選中要渲染的頁面元素,本例中是一個div
-
data:數據,數據是一個對象,裏面有很多屬性,都可以渲染到視圖中 -
頁面中的 h2
元素中,通過{{name}}
的方式,來渲染剛剛定義的name
屬性
更神奇的在於,當你修改name
屬性時,頁面會跟着變化。
雙向綁定
對剛纔的案例進行簡單修改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="num">
<h2>
{{name}},非常酷!
有{{num}}個酷炫功能!
</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el: "#app", // el即element,要渲染的頁面元素
data: {
name: "Vue測試",
num: 1
}
});
</script>
</body>
</html>
-
在 data
中添加了新的屬性:num
-
在頁面中有一個 input
元素,通過v-model
與num
進行綁定 -
同時通過 {{num}}
在頁面輸出
可以觀察到,輸入框的變化引起了data
中的num
的變化,同時頁面輸出也跟着變化。
-
input
與num
綁定,input
的value
值變化,影響到了data
中的num
值 -
頁面 {{num}}
與數據num
綁定,因此num
值變化,引起了頁面效果變化。
沒有任何dom
操作,這就是雙向綁定的魅力。
事件處理
在頁面添加一個按鈕:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="num">
<button v-on:click="num++">點我</button>
<h2>
{{name}},非常酷!
有{{num}}個酷炫功能!
</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el: "#app", // el即element,要渲染的頁面元素
data: {
name: "Vue測試",
num: 1
}
});
</script>
</body>
</html>
-
這裏用 v-on
指令綁定點擊事件,而不是普通的onclick
,然後直接操作num
-
普通 onclick
是無法直接操作num
的
Vue 實例
創建Vue實例
每個 Vue
應用都是通過用 Vue
函數創建一個新的 Vue
實例開始的:
var vm = new Vue({
// 選項
})
在構造函數中傳入一個對象,並且在對象中聲明各種Vue需要的數據和方法,包括:
-
el -
data -
methods -
… …等
接下來,一一介紹。
模板或元素
每個Vue
實例都需要關聯一段Html
模板,Vue
會基於此模板進行視圖渲染;可以通過el屬性來指定。
例如一段html模板:
<div id="app">
</div>
然後創建Vue
實例,關聯這個div
var vm = new Vue({
el:"#app"
})
這樣,Vue
就可以基於id
爲 app
的div
元素作爲模板進行渲染了。在這個div
範圍以外的部分是無法使用vue
特性的。
數據
當Vue實例被創建時,它會嘗試獲取在data中定義的所有屬性,用於視圖的渲染,並且監視data中的屬性變化,當data發生改變,所有相關的視圖都將重新渲染,這就是“響應式“系統。
html:
<div id="app">
<input type="text" v-model="name"/>
</div>
js:
var vm = new Vue({
el:"#app",
data:{
name:"ZHANGSAN"
}
})
-
name
的變化會影響到input
的值 -
input
中輸入的值,也會導致vm
中的name
發生改變
方法
Vue實例中除了可以定義data屬性,也可以定義方法,並且在Vue的作用範圍內使用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button v-on:click="add">點我</button>
</div>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {},
methods: {
add: function () {
console.log("點我了...233")
}
}
});
</script>
</body>
</html>
生命週期鉤子函數
每個 Vue 實例在被創建時都要經過一系列的初始化過程 :創建實例,裝載模板,渲染模板等。Vue爲生命週期中的每個狀態都設置了鉤子函數(監聽函數)。每當Vue實例處於不同的生命週期時,對應的函數就會被觸發調用。
所有的生命週期鉤子自動綁定 this 上下文到實例中,因此你可以訪問數據,對屬性和方法進行運算。
生命週期
每個 Vue 實例在被創建之前都要經過一系列的初始化過程
生命週期函數 | 含義 |
---|---|
beforeCreate(vue對象創建前) | 組件實例剛被創建,組件屬性計算之前,比如data屬性等 |
created(創建後) | 模板編譯、掛載之前 |
mounted(載入後) | 模板編譯、掛載之後 |
beforeUpdate(更新前) | 組件更新之前 |
updated(更新後) | 組件更新之後 |
beforeDestroy(銷燬前) | 組件銷燬前調用 |
destroyed(銷燬後) | 組件銷燬後調用 |
vm.$el :Vue 實例使用的根 DOM 元素
vm.$root :當前的 Vue 實例。
Vue在實例化的過程中,會調用這些生命週期的鉤子,給我們提供了執行自定義邏輯的機會。那麼,在這些vue鉤子中,vue實例到底執行了那些操作,我們先看下面執行的例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs生命週期</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{message}}
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
message: 'hello world'
},
beforeCreate: function () {
console.log(this);
showData('創建vue實例前', this);
},
created: function () {
showData('創建vue實例後', this);
},
beforeMount: function () {
showData('掛載到dom前', this);
},
mounted: function () {
showData('掛載到dom後', this);
},
beforeUpdate: function () {
showData('數據變化更新前', this);
},
updated: function () {
showData('數據變化更新後', this);
},
beforeDestroy: function () {
showData('vue實例銷燬前', this);
},
destroyed: function () {
showData('vue實例銷燬後', this);
}
});
function showData(process, obj) {
console.log(process);
console.log('data 數據:' + obj.message)
console.log('掛載的對象:')
console.log(obj.$el)
}
</script>
</body>
</html>
鉤子函數
例如:created
代表在vue實例創建後;
可以在Vue
中定義一個created
函數,代表這個時期的構造函數:
創建示例html頁面如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{msg}}
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
// 初始化爲空
msg: ""
},
// 鉤子函數
created() {
// this 表示Vue實例
this.msg = "hello vue.created";
console.log(this)
}
});
</script>
</body>
</html>
this
可以看下在vue內部的this變量是誰,在created的時候,打印this
var app = new Vue({
el:"#app",
data:{
msg: ""
},
//鉤子函數
created() {
//this表示vue實例
this.msg = "hello vue. created.";
console.log(this);
}
});
控制檯的輸出;總結:this 就是當前的Vue實例,在Vue對象內部,必須使用 this 才能訪問到Vue中定義的data內屬性、方法等。
應用場景
-
在beforeCreate生命週期函數運行時,可以添加loading動畫
-
在created生命週期函數運行時,可以結束loading動畫,還可以做一些初始化,實現函數自執行等操作
-
最經常使用的是mounted生命週期函數
可以發起後端數據請求,取回數據
可以接收頁面之間傳遞的參數
可以子組件向父組件傳遞參數等
指令
什麼是指令?
指令 (Directives) 是帶有 v-
前綴的特殊屬性。例如在入門案例中的v-model
,代表雙向綁定。
插值表達式
花括號
格式:
{{表達式}}
說明:
-
該表達式支持JS語法,可以調用js內置函數( 必須有返回值) -
表達式必須有返回結果。例如 1 + 1,沒有結果的表達式不允許使用,如:var a = 1 + 1; -
可以直接獲取Vue實例中定義的數據或函數
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{msg}}
</div>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
msg: "hello vue."
}
});
</script>
</body>
</html>
插值閃爍
使用{{}}
方式在網速較慢時會出現問題。在數據未加載完成時,頁面會顯示出原始的 {{}}
,加載完畢後才顯示正確數據,稱爲插值閃爍。解決辦法是通過v-text和v-html替換
v-text 和 v-html
使用v-text
和v-html
指令來替代 {{}}
說明:
-
v-text
:將數據輸出到元素內部,如果輸出的數據有HTML代碼,會作爲普通文本輸出 -
v-html
:將數據輸出到元素內部,如果輸出的數據有HTML代碼,會被渲染
示例,改造原頁面內容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--{{msg}}-->
v-text:<span v-text="msg"></span><br/>
v-html:<span v-html="msg"></span><br/>
</div>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
// msg: "hello vue."
msg: "<h2>hello vue.</h2>"
}
});
</script>
</body>
</html>
並且不會出現插值閃爍,當沒有數據時,會顯示空白。
v-model
剛纔的v-text
和v-html
可以看做是單向綁定,數據影響了視圖渲染,但是反過來就不行。
接下來學習的v-model
是雙向綁定,視圖(View
)和模型(Model
)之間會互相影響。
既然是雙向綁定,一定是在視圖中可以修改數據,這樣就限定了視圖的元素類型。
目前v-model
的可使用元素有:
-
input -
select -
textarea -
checkbox -
radio -
components(Vue中的自定義組件)
基本上除了最後一項,其它都是表單的輸入項。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="checkbox" value="Java" v-model="language">Java<br/>
<input type="checkbox" value="PHP" v-model="language">PHP<br/>
<input type="checkbox" value="Swift" v-model="language">Swift<br/>
<h2>
你選擇了:{{language.join(",")}}
</h2>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
language: []
}
});
</script>
</body>
</html>
-
多個 checkbox
對應一個model
時,model
的類型是一個數組,單個checkbox
值是boolean類型 -
radio
對應的值是input的value值 -
input
和textarea
默認對應的model是字符串 -
select
單選對應字符串,多選對應也是數組
v-on
基本用法
v-on
指令用於給頁面元素綁定事件。
語法:
v-on:事件名="js片段或函數名"
簡寫語法:
@事件名="js片段或函數名"
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--直接寫js片段-->
<button @click="num++">增加</button>
<!--使用函數名,該函數必須要在vue實例中定義-->
<button @click="decrement">減少</button>
<h2>
num={{num}}
</h2>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
num: 1
},
methods: {
decrement() {
this.num--;
}
}
});
</script>
</body>
</html>
事件修飾符
在事件處理程序中調用 event.preventDefault()
或 event.stopPropagation()
是非常常見的需求。
儘管我們可以在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的數據邏輯,而不是去處理 DOM 事件細節。
爲了解決這個問題,Vue.js 爲 v-on
提供了事件修飾符。之前提過,修飾符是由點開頭的指令後綴來表示的。
-
.stop
:阻止事件冒泡 -
.prevent
:阻止默認事件發生 -
.capture
:使用事件捕獲模式 -
.self
:只有元素自身觸發事件才執行。(冒泡或捕獲的都不執行) -
.once
:只執行一次
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--直接寫js片段-->
<button @click="num++">增加</button>
<!--使用函數名,該函數必須要在vue實例中定義-->
<button @click="decrement">減少</button>
<h2>
num={{num}}
</h2>
<hr>
事件冒泡測試:<br/>
<div style="background-color: lightblue;width: 100px;height: 100px" @click="print('div被點擊了')">
<button @click.stop="print('點擊了button')">點我試試</button>
</div>
<br>阻止默認事件:<br>
<a href="http://www.baidu.com" @click.prevent="print('點擊了超鏈接')">百度</a>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
num: 1
},
methods: {
decrement() {
this.num--;
},
print(str) {
console.log(str);
}
}
});
</script>
</body>
</html>
v-for
遍歷數據渲染頁面是非常常用的需求,Vue中通過v-for
指令來實現。
遍歷數組
語法:
v-for="item in items"
-
items
:要遍歷的數組,需要在vue的data中定義好。 -
item
:循環變量
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="user in users">
{{user.name}} -- {{user.age}} -- {{user.gender}}
</li>
</ul>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
users: [
{"name": "小明", "age": 13, "gender": "男"},
{"name": "小紅", "age": 13, "gender": "女"},
{"name": "小綠", "age": 4, "gender": "男"}
]
}
});
</script>
</body>
</html>
數組角標
在遍歷的過程中,如果需要知道數組角標,可以指定第二個參數:
語法:
v-for="(item,index) in items"
-
items:要迭代的數組 -
item:迭代得到的數組元素別名 -
index:迭代到的當前元素索引,從0開始。
示例:
<div id="app">
<ul>
<li v-for="(user,index) in users">
{{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
</li>
</ul>
</div>
遍歷對象
v-for
除了可以迭代數組,也可以迭代對象。語法基本類似
語法:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
-
1個參數時,得到的是對象的值 -
2個參數時,第一個是值,第二個是鍵 -
3個參數時,第三個是索引,從0開始 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="user in users">
{{user.name}} -- {{user.age}} -- {{user.gender}}
</li>
</ul>
<hr>
<ul>
<li v-for="(user,index) in users">
{{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
</li>
</ul>
<hr>
<ul>
<!--1個參數時,得到的是對象的值-->
<li v-for="value in person">
{{value}}
</li>
</ul>
<hr>
<ul>
<!--2個參數時,第一個是值,第二個是鍵-->
<li v-for="(value,key ) in person">
{{value}}--{{key}}
</li>
</ul>
<hr>
<ul>
<!--3個參數時,第三個是索引,從0開始-->
<li v-for="(value,key,index ) in person">
{{value}}--{{key}} -- {{index}}
</li>
</ul>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
users: [
{"name": "小明", "age": 13, "gender": "男"},
{"name": "小紅", "age": 13, "gender": "女"},
{"name": "小綠", "age": 4, "gender": "男"}
],
person: {"name": "zhangsan", "age": 13, "gender": "男", "address": "中國"}
}
});
</script>
</body>
</html>
key
當 Vue.js 用 v-for 正在更新已渲染過的元素列表時,它默認用“就地複用”策略。如果數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單複用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。
如果使用key這個功能可以有效的提高渲染的效率;key一般使用在遍歷完後,又增、減集合元素的時候更有意義。
但是要實現這個功能,你需要給Vue一些提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要爲每項提供一個唯一 key 屬性。理想的 key 值是每項都有的且唯一的 id。也就是key是該的唯一標識。
示例:
<ul>
<li v-for="(item,index) in items" :key="index"></li>
</ul>
-
這裏使用了一個特殊語法: :key=""
後面會講到,它可以讓你讀取vue中的屬性,並賦值給key屬性 -
這裏綁定的key是數組的索引,應該是唯一的
v-if 和 v-show
基本使用
v-if
,顧名思義,條件判斷。當得到結果爲true時,所在的元素纔會被渲染。
語法:
v-if="布爾表達式"
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="show = !show">點我</button>
<h2 v-if="show">
hello vuejs.
</h2>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
show: true
}
});
</script>
</body>
</html>
與 v-for 結合
當v-if
和v-for
出現在一起時,v-for
優先級更高。也就是說,會先遍歷,再判斷條件。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="show = !show">點我</button>
<h2 v-if="show">
hello vuejs.
</h2>
<hr>
<ul>
<li v-for="(user,index) in users" v-if="user.gender=='女'" :key="index" style="background-color: deeppink">
{{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
</li>
</ul>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
show: true,
users: [
{"name": "北京大學", "age": 8, "gender": "男"},
{"name": "清華大學", "age": 12, "gender": "女"},
{"name": "復旦大學", "age": 4, "gender": "男"},
{"name": "南開大學", "age": 2, "gender": "女"}
]
}
});
</script>
</body>
</html>
v-else
可以使用 v-else
指令來表示 v-if
的“else 塊
”:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="show = !show">點我</button>
<h2 v-if="show">
hello vuejs.
</h2>
<hr>
<ul v-if="show">
<li v-for="(user,index) in users" v-if="user.gender=='女'" :key="index" style="background-color: deeppink">
{{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
</li>
<li v-else style="background-color: blue">
{{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
</li>
</ul>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
show: true,
users: [
{"name": "北京大學", "age": 8, "gender": "男"},
{"name": "清華大學", "age": 12, "gender": "女"},
{"name": "復旦大學", "age": 4, "gender": "男"},
{"name": "南開大學", "age": 2, "gender": "女"}
]
}
});
</script>
</body>
</html>
v-else
元素必須緊跟在帶 v-if
或者 v-else-if
的元素的後面,否則它將不會被識別。v-else-if
,顧名思義,充當 v-if 的“else-if 塊
”,可以連續使用:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
類似於 v-else
, v-else-if
也必須緊跟在帶 v-if 或者 v-else-if 的元素之後。
v-show
另一個用於根據條件展示元素的選項是 v-show
指令。用法大致一樣:
<h1 v-show="ok">Hello!</h1>
不同的是帶有 v-show 的元素始終會被渲染並保留在 DOM 中。v-show 只是簡單地切換元素的 CSS 屬性display
。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="show = !show">點我</button>
<h2 v-if="show">
hello vuejs.
</h2>
<hr>
<ul v-if="show">
<li v-for="(user,index) in users" v-if="user.gender=='女'" :key="index" style="background-color: deeppink">
{{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
</li>
<li v-else style="background-color: blue">
{{index}} -- {{user.name}} -- {{user.age}} -- {{user.gender}}
</li>
</ul>
<hr>
<h2 v-show="show">
你好,世界!
</h2>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
show: true,
users: [
{"name": "北京大學", "age": 8, "gender": "男"},
{"name": "清華大學", "age": 12, "gender": "女"},
{"name": "復旦大學", "age": 4, "gender": "男"},
{"name": "南開大學", "age": 2, "gender": "女"}
]
}
});
</script>
</body>
</html>
v-bind
屬性上使用vue數據
看這樣一個案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
<style type="text/css">
div {
width: 100px;
height: 100px;
color: white;
}
.red {
background-color: red;
}
.blue {
background-color: blue;
}
</style>
</head>
<body>
<div id="app">
<button @click="color='red'">紅色</button>
<button @click="color='blue'">藍色</button>
<div :class="">
點擊按鈕改變背景顏色
</div>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
color: "red"
}
});
</script>
</body>
</html>
解讀:
-
頁面有兩個按鈕,點擊時,會改變Vue實例中的color值,這個值與前面定義的CSS樣式一致。 -
目前div的 class
爲空,希望實現點擊按鈕後,div的class樣式會在.red和.blue之間切換
該如何實現?
大家可能會這麼想,既然color值會動態變化爲不同的class名稱,那麼我們直接把color注入到class屬性就好了,於是就這樣寫:
<div class="{{color}}"></div>
這樣寫是錯誤的!因爲插值表達式不能用在標籤的屬性中。
此時,Vue提供了一個新的指令來解決:v-bind
,語法:
v-bind:屬性名="Vue中的變量"
例如,在這裏我們可以寫成:
<div v-bind:class="color"></div>
不過,v-bind
太麻煩,因此可以省略,直接寫成:
,:屬性名='屬性值'
,即:
<div :class="color"></div>
class屬性的特殊用法
上面雖然實現了顏色切換,但是語法卻比較囉嗦。
Vue對class屬性進行了特殊處理,可以接收數組或對象格式
對象語法:可以傳給 :class
一個對象,以動態地切換 class:
<div :class="{ red: true,blue:false }"></div>
-
對象中,key是已經定義的class樣式的名稱,如本例中的:red 和 blue -
對象中,value是一個布爾值,如果爲true,則這個樣式會生效,如果爲false,則不生效。
之前的案例可以改寫成這樣:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
<style type="text/css">
div {
width: 100px;
height: 100px;
color: white;
}
.red {
background-color: red;
}
.blue {
background-color: blue;
}
</style>
</head>
<body>
<div id="app">
<button @click="color='red'">紅色</button>
<button @click="color='blue'">藍色</button>
<!--v-bind 簡寫爲 :-->
<div :class="color">
點擊按鈕改變背景顏色
</div>
<hr>
<br>
<button @click="bool=!bool">點我改變下面色塊的顏色</button>
<div :class="{red:bool, blue:!bool}">
點擊按鈕改變背景顏色
</div>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
color: "red",
bool:true
}
});
</script>
</body>
</html>
首先class綁定的是一個對象:{red:bool, blue: !bool}
red和blue兩個樣式的值分別是bool和!bool,也就是說這兩個樣式的生效標記恰好相反,一個生效,另一個失效。
bool默認爲true,也就是說默認red生效,blue不生效
現在只需要一個按鈕即可,點擊時對bool取反,自然實現了樣式的切換
計算屬性
在插值表達式中使用js表達式是非常方便的,而且也經常被用到。
但是如果表達式的內容很長,就會顯得不夠優雅,而且後期維護起來也不方便,例如下面的場景,有一個日期的數據,但是是毫秒值:
data:{
birthday:1429032123201 // 毫秒值
}
在頁面渲染,希望得到yyyy-MM-dd的樣式則需要如下處理:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>
你的生日是:
{{new Date(birthday).getFullYear()}}-{{new Date(birthday).getMonth()+1}}-{{new Date(birthday).getDay()}}
</h2>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
birthday: 1429032123201
}
});
</script>
</body>
</html>
雖然能得到結果,但是非常麻煩。
Vue中提供了計算屬性,來替代複雜的表達式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>
你的生日是:
{{new Date(birthday).getFullYear()}}-{{new Date(birthday).getMonth()+1}}-{{new Date(birthday).getDay()}}
</h2>
<hr>
<h2>
computed計算方式;你的生日爲:{{birth}}
</h2>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
birthday: 1429032123201
},
computed: {
birth() {
const date = new Date(this.birthday);
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDay();
}
}
});
</script>
</body>
</html>
計算屬性本質就是方法,但是一定要返回數據。然後頁面渲染時,可以把這個方法當成一個變量來使用。
過濾器
說明
官方說法:Vue.js 允許你自定義過濾器,可被用於一些常見的==文本格式化==。過濾器可以用在兩個地方:雙花括號插值和 v-bind 表達式 (後者從 2.1.0+ 開始支持)。過濾器應該被添加在 JavaScript 表達式的尾部,由“管道”符號指示。
通俗的來說:過濾器是對即將顯示的數據做進一步的篩選處理,然後進行顯示,值得注意的是過濾器並沒有改變原來的數據,只是在原數據的基礎上產生新的數據。
過濾器的種類:
-
全局過濾器 -
局部過濾器
過濾器的使用步驟
定義過濾器
-
全局過濾器
Vue.filter('過濾器名稱', function (value[,param1,...] ) {
//邏輯代碼
}) -
局部過濾器
new Vue({
filters: {
'過濾器名稱': function (value[,param1,...] ) {
// 邏輯代碼
}
}
})
應用過濾器
{{ 表達式 | 過濾器名字}}
過濾器不帶參數
案例1:日期格式化
<body>
<div id="myDiv">
{{birthday | dateFormat }}
</div>
<script src="../js/vue.js"></script>
<script src="../js/moment.js"></script> // 需要引入一個日期處理的一個工具類
<script type="text/javascript">
// 定義過濾器
Vue.filter("dateFormat",function(value){
return moment(value).format("YYYY-MM-DD HH:mm:ss");
});
const app = new Vue({
el: "#myDiv",
data: {
birthday: new Date()
}
});
</script>
</body>
案例2:文本格式化
<body>
<div id="myDiv">
{{message | messageFormat}} <br/>
{{message }} // 值並沒有改變
</div>
<script src="../js/vue.js"></script>
<script type="text/javascript">
Vue.filter("messageFormat",function(value){
return value.replace("很棒","優秀");
});
const app = new Vue({
el: "#myDiv",
data: {
message: "HelloWorld,是一個很棒的青年"
}
});
</script>
</body>
過濾器帶參數
<body>
<div id="myDiv">
{{birthday | dateFormat }} <br/> // 不傳遞格式化標準就用完整寫法
{{birthday | dateFormat("YYYY-MM-DD") }} // 傳入了格式化標準就用傳遞的寫法進行格式化
</div>
<script src="../js/vue.js"></script>
<script src="../js/moment.js"></script>
<script type="text/javascript">
Vue.filter("dateFormat",function(value,pattern = "YYYY-MM-DD HH:mm:ss"){
return moment(value).format(pattern);
});
const app = new Vue({
el: "#myDiv",
data: {
birthday: new Date()
}
});
</script>
</body>
watch
監控
watch
可以讓我們監控一個值的變化。從而做出相應的反應。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input v-model="message">
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
message: "hello vue"
},
watch: {
message(newValue, oldValue) {
console.log("新值:" + newValue + ";舊值:" + oldValue)
}
}
});
</script>
</body>
</html>
深度監控
如果監控的是一個對象,需要進行深度監控,才能監控到對象中屬性的變化,例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuejs測試</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="message">
<hr>
<br>
<input type="text" v-model="person.name"><br>
<input type="text" v-model="person.age">
<button @click="person.age++">+</button>
<h2>
姓名爲:{{person.name}};年齡爲:{{person.age}}
</h2>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
message: "hello vue",
person: {"name": "zhangsan", "age": 12}
},
watch: {
message(newValue, oldValue) {
console.log("新值:" + newValue + ";舊值:" + oldValue)
},
person: {
// 開啓深度監控,可以監控到對象屬性值的變化
deep: true,
// 監控的處理辦法
handler(obj) {
console.log("name = " + obj.name + ", age=" + obj.age);
}
}
}
});
</script>
</body>
</html>
變化:
以前定義監控時,person是一個函數,現在改成了對象,並且要指定兩個屬性:
-
deep
:代表深度監控,不僅監控person變化,也監控person中屬性變化 -
handler
:就是以前的監控處理函數
組件化
在大型應用開發的時候,頁面可以劃分成很多部分。往往不同的頁面,也會有相同的部分。例如可能會有相同的頭部導航。
但是如果每個頁面都獨自開發,這無疑增加了我們開發的成本。所以我們會把頁面的不同部分拆分成獨立的組件,然後在不同頁面就可以共享這些組件,避免重複開發。
全局組件
我們通過Vue的component方法來定義一個全局組件。
<div id="app">
<!--使用定義好的全局組件-->
<counter></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
// 定義全局組件,兩個參數:1,組件名稱。2,組件參數
Vue.component("counter",{
template:'<button v-on:click="count++">你點了我 {{ count }} 次,我記住了.</button>',
data(){
return {
count:0
}
}
})
var app = new Vue({
el:"#app"
})
</script>
-
組件其實也是一個Vue實例,因此它在定義時也會接收:data、methods、生命週期函數等 -
不同的是組件不會與頁面的元素綁定,否則就無法複用了,因此沒有el屬性。 -
但是組件渲染需要html模板,所以增加了template屬性,值就是HTML模板 -
全局組件定義完畢,任何vue實例都可以直接在HTML中通過組件名稱來使用組件了。 -
data必須是一個函數,不再是一個對象。
組件的複用
定義好的組件,可以任意複用多次:
<div id="app">
<!--使用定義好的全局組件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
你會發現每個組件互不干擾,都有自己的count值。怎麼實現的?
組件的data屬性必須是函數!
當我們定義這個counter組件時,它的data 並不是像這樣直接提供一個對象:
data: {
count: 0
}
取而代之的是,一個組件的 data 選項必須是一個函數,因此每個實例可以維護一份被返回對象的獨立的拷貝:
data: function () {
return {
count: 0
}
}
如果 Vue 沒有這條規則,點擊一個按鈕就會影響到其它所有實例!
局部註冊
一旦全局註冊,就意味着即便以後你不再使用這個組件,它依然會隨着Vue的加載而加載。
因此,對於一些並不頻繁使用的組件,我們會採用局部註冊。
我們先在外部定義一個對象,結構與創建組件時傳遞的第二個參數一致:
const counter = {
template:'<button v-on:click="count++">你點了我 {{ count }} 次,我記住了.</button>',
data(){
return {
count:0
}
}
};
然後在Vue中使用它:
var app = new Vue({
el:"#app",
components:{
counter:counter // 將定義的對象註冊爲組件
}
})
-
components就是當前vue對象子組件集合。 -
其key就是子組件名稱 -
其值就是組件對象的屬性 -
效果與剛纔的全局註冊是類似的,不同的是,這個counter組件只能在當前的Vue實例中使用
組件通信
通常一個單頁應用會以一棵嵌套的組件樹的形式來組織:
-
頁面首先分成了頂部導航、左側內容區、右側邊欄三部分 -
左側內容區又分爲上下兩個組件 -
右側邊欄中又包含了3個子組件
各個組件之間以嵌套的關係組合在一起,那麼這個時候不可避免的會有組件間通信的需求。
父向子傳遞
-
父組件使用子組件時,自定義屬性(屬性名任意,屬性值爲要傳遞的數據) -
子組件通過props接收父組件屬性
父組件使用子組件,並自定義了title屬性:
<div id="app">
<h1>打個招呼:</h1>
<!--使用子組件,同時傳遞title屬性-->
<introduce title="父組件傳遞的消息"/>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component("introduce",{
// 直接使用props接收到的屬性來渲染頁面
template:'<h1>{{title}}</h1>',
props:['title'] // 通過props來接收一個父組件傳遞的屬性
})
var app = new Vue({
el:"#app"
})
</script>
傳遞複雜數據
我們定義一個子組件,並接受複雜數據:
const myList = {
template: '\
<ul>\
<li v-for="item in items" :key="item.id">{{item.id}} : {{item.name}}</li>\
</ul>\
',
props: {
items: {
type: Array,
default: [],
required: true
}
}
};
-
這個子組件可以對 items 進行迭代,並輸出到頁面。 -
props:定義需要從父組件中接收的屬性 -
type:限定父組件傳遞來的必須是數組 -
default:默認值 -
required:是否必須 -
items:是要接收的屬性名稱
我們在父組件中使用它:
<div id="app">
<h2>百知已開設如下課程:</h2>
<!-- 使用子組件的同時,傳遞屬性,這裏使用了v-bind,指向了父組件自己的屬性lessons -->
<my-list :items="lessons"/>
</div>
var app = new Vue({
el:"#app",
components:{
myList // 當key和value一樣時,可以只寫一個
},
data:{
lessons:[
{id:1, name: 'java'},
{id:2, name: 'python'},
{id:3, name: 'ui'},
]
}
})
子向父的通信
來看這樣的一個案例:
<div id="app">
<h2>num: {{num}}</h2>
<!--使用子組件的時候,傳遞num到子組件中-->
<counter :num="num"></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component("counter", {// 子組件,定義了兩個按鈕,點擊數字num會加或減
template:'\
<div>\
<button @click="num++">加</button> \
<button @click="num--">減</button> \
</div>',
props:['num']// count是從父組件獲取的。
})
var app = new Vue({
el:"#app",
data:{
num:0
}
})
</script>
-
子組件接收父組件的num屬性 -
子組件定義點擊按鈕,點擊後對num進行加或減操作
我們嘗試運行,好像沒問題,點擊按鈕試試:
子組件接收到父組件屬性後,默認是不允許修改的。怎麼辦?
既然只有父組件能修改,那麼加和減的操作一定是放在父組件:
var app = new Vue({
el:"#app",
data:{
num:0
},
methods:{ // 父組件中定義操作num的方法
increment(){
this.num++;
},
decrement(){
this.num--;
}
}
})
但是,點擊按鈕是在子組件中,那就是說需要子組件來調用父組件的函數,怎麼做?
我們可以通過v-on指令將父組件的函數綁定到子組件上:
<div id="app">
<h2>num: {{num}}</h2>
<counter :count="num" @inc="increment" @dec="decrement"></counter>
</div>
在子組件中定義函數,函數的具體實現調用父組件的實現,並在子組件中調用這些函數。當子組件中按鈕被點擊時,調用綁定的函數:
Vue.component("counter", {
template:'\
<div>\
<button @click="plus">加</button> \
<button @click="reduce">減</button> \
</div>',
props:['count'],
methods:{
plus(){
this.$emit("inc");
},
reduce(){
this.$emit("dec");
}
}
})
-
vue提供了一個內置的this.$emit()函數,用來調用父組件綁定的函數
❤️ 帥氣的你又來看了我
如果你覺得這篇內容對你挺有有幫助的話:
-
點贊支持下吧,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓 -_-)
-
歡迎在留言區與我分享你的想法,也歡迎你在留言區記錄你的思考過程。
-
覺得不錯的話,也可以關注 編程鹿 的個人公衆號看更多文章和講解視頻(感謝大家的鼓勵與支持🌹🌹🌹)
本文分享自微信公衆號 - 鹿小洋的Java筆記(lulaoshiJava)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。