1概述
上一章我們說了Vue是用于構(gòu)建用戶界面的框架,它最終呈現(xiàn)給用戶的是一個一個HTML標(biāo)簽。Vue可以使用HTML模板和render函數(shù)這2種方式來構(gòu)建這些HTML標(biāo)簽。當(dāng)開發(fā)者使用了HTML模板來構(gòu)建界面時,Vue內(nèi)部會將其編譯成render函數(shù),最后渲染到掛載點上。render函數(shù)生成的是虛擬DOM,虛擬DOM是基于js的,相比于DOM的操作,開銷會小很多。
Vue是實例需要一個掛載點,這個掛載點是由Vue的el選項指定的,el的類型可以是string或HTMLElement類型,當(dāng)為string時,使用css選擇器。
<html>
<body>
<div id="app">
<h1>Hello World!</h1>
</div>
<script>
var vm = new Vue({
el:"#app"
});
</script>
</body>
</html>
上面使用了el類型為string的方式查找掛載點,#app為css的id選擇器。Vue實例會將自身生成的DOM替換掉掛載點的元素,這里我們沒有定義如何生成DOM,所以,Vue會將掛載點的HTML提取出來當(dāng)作模板生成DOM,替換掉原來的HTML標(biāo)簽。
2 HTML模板
HTML模板有4種方式定義,分別是:
Vue實例不定義,由Vue將掛載點的元素提取出來生成模板,可以理解為將模板寫在掛載點內(nèi),如上面的例子。
-
將模板以字符串的形式寫在實例的配置選項template中
<div id="app"></div> <script> var vm = new Vue({ el:"#app", template:"<h1>Hello World!</h1>" }); </script>實例掛載后,會將template中的字符串,轉(zhuǎn)換為html標(biāo)簽,替換掉app的內(nèi)容。
-
使用x-template,在template中定義模板有些弊端,因為它是字符串,所以缺少高亮的語法支持,當(dāng)需要多行顯示是,需要用到
\,體驗不好。x-template是將模板的定義放在scirpt標(biāo)簽中,然后在template中使用#引用該模板。<div id="app"></div> <!--使用x-template定義模板--> <script type="text/x-template" id="tempHello"> <h1>Hello World!</h1> </script> <!--使用template選項引用x-template定義的模板--> <script> var vm = new Vue({ el:"#app", template:"#tempHello", }); </script>在定義x-template模板時,需要注意的是,x-template定義的模板在script標(biāo)簽中,type屬性為
text/x-template,需要配上id,后續(xù)的template中才能夠引用。在template選項中要使用#引用x-template模板(類似使用css的id選擇器)。 使用單文件組件(single-file components)的方式定義模板(最常用),單文件組件是以
.vue結(jié)尾的文件,一個文件就是一個組件,后續(xù)講解組件時再詳細說明。
3 內(nèi)部指令(directive)
在HTML模板中,html元素僅僅是界面的呈現(xiàn),我們還需要和vue實例進行交互,這就需要用到vue的指令。在vue中,指令是寫在html元素中,以v-開頭,后接表達式(有些不需要表達式),它是Vue實例數(shù)據(jù)與用戶界面之間的紐帶。如:
<div id="app">
<p v-text="msg">text</p>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"Hello World!",
}
});
</script>
v-text指令后接表達式msg,msg是對實例data中Hello World!的引用。當(dāng)實例運行時,v-text指令所在的標(biāo)簽內(nèi)容會替換為Hello World!。有些指令可以在其后加上修飾符,從而達到特殊的效果。指令大致可以分為顯示類、渲染類,事件類3種。
3.1 顯示類指令
| 指令 | 用法 | 說明 |
|---|---|---|
| v-text | <div v-text="msg"></div> |
以文本的方式更新元素的內(nèi)容,即使msg中是html標(biāo)簽,它只當(dāng)做字符串的方式處理,不會解析標(biāo)簽。即,只改變元素的textContent,與插值表達式相同<div>{{msg}}</div>
|
| v-html | <div v-html=“html”></div> |
更新元素的 innerHTML 。注意:內(nèi)容按普通 HTML 插入 - 不會作為 Vue 模板進行編譯 |
| v-cloak | <div v-cloak>{{msg}}</div> |
v-cloak不接表達式,當(dāng)網(wǎng)速較慢時,vue實例還沒有加載完成,這時界面上會顯示{{msg}},在vue實例加載完成后,{{msg}}會替換為對應(yīng)的內(nèi)容,不過這使得頁面會閃動。當(dāng)使用v-cloak指令后,vue實例在編譯完成后,會移除該指令。通過這一特征,使用v-cloak再配合css的屬性選擇器[v-cloak]{display:none;}使用,達到在網(wǎng)速慢時界面不閃動的效果。 |
3.2 渲染類指令
| 指令 | 用法 | 說明 |
|---|---|---|
| v-once | <div v-once>{{msg}}</div> |
v-once不接表達式,通常配合其他指令使用,表示只渲染一次。渲染后,當(dāng)msg的內(nèi)容發(fā)生變化時,也不會重新渲染。 |
| v-show | <div v-show=“show”></div> |
v-show后接表達式,表達式返回true或false,根據(jù)表達式的值,修改該元素的display的樣式,從而達到顯示隱藏的效果。注意:因為是使用的display樣式,所以該元素始終都會被渲染,只是顯示/隱藏而已。 |
v-if v-if-else v-else
v-if、v-if-else和v-else標(biāo)簽需要組合使用,后接表達式。與其它語言的if/else一樣,表達式條件為true時進入該分支。
<div id="app">
<p v-if="status==0">0</p>
<p v-else-if="status==1">1</p>
<p v-else="status==2">2</p>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
status:0,
}
});
</script>
與v-show不同,使用這組指令時,當(dāng)條件為true時才渲染該分支對應(yīng)的元素/組件,當(dāng)條件為false時,對應(yīng)的元素/組件會被移除。
Vue會出于效率考慮,會復(fù)用已有的元素,而非重新渲染,這樣做有時會出現(xiàn)不符合需求的情況。比如,當(dāng)復(fù)用input的時候,不會改變input的value值。
<div id="app">
<template v-if="showQQ">
<label>QQ</label>
<input placeholder="QQ">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Email">
</template>
</div>
當(dāng)showQQ為true時,顯示QQ輸入框,用戶在輸入內(nèi)容后,切換到Email,即改變了showQQ的值為false,這時第一個template的內(nèi)容將會被移除,但是vue為了提高效率,會重新引用這個label和input,并設(shè)置了對應(yīng)的值,不過,vue并沒有修改input的value的值,所以Email輸入框中的值還是我們之前輸入的QQ內(nèi)容。
要解決這個問題,只需要在input上加入key屬性,告訴vue這兩個input是完全獨立的,不要復(fù)用它們,注意key的值必須是唯一的。
v-if和v-show的區(qū)別:
- v-if是條件渲染,v-show是條件顯示,v-if會根據(jù)條件銷毀元素/組件以及其綁定事件,而v-show總是會渲染,根據(jù)條件切換css樣式來控制顯示/隱藏。
- v-show不能使用在template標(biāo)簽上
v-for
v-for通過遍歷數(shù)據(jù)源渲染指定內(nèi)容。
<ul>
<li v-for="person in persons">{{person.name}}</li>
</ul>
<script>
var vm = new Vue({
data:{
persons:[{name:"張三"},{name:"李四"},]
}
});
</script>
將v-for加到要渲染的標(biāo)簽上,使用alias in expression語法為當(dāng)前遍歷的元素提供別名。v-for的數(shù)據(jù)源可以是數(shù)組,也可以是對象,還可以是數(shù)字。
- 當(dāng)數(shù)據(jù)源為數(shù)組時,別名指向的是數(shù)組元素,
- 當(dāng)數(shù)據(jù)源為對象時,別名指向的是對象的值集合中的值。
- 當(dāng)數(shù)據(jù)源為數(shù)字時,別名指向的是從1開始的數(shù)字,迭代到等于數(shù)據(jù)源的數(shù)字
v-for有多種可選參數(shù)形式:
<div v-for="item in array"></div>
<div v-for="(item,index) in array"></div>
<div v-for="value in object"></div>
<div v-for="(value,key) in object"></div>
<div v-for="(value,key,index) in object"></div>
<div v-for="n in number"></div>
3.3 事件類指令
事件類指令包括v-on,v-bind,v-model3個指令,如果說顯示類指令和渲染類指令負責(zé)界面的呈現(xiàn),那么事件類指令就負責(zé)定義交互邏輯,這些指令非常常用。
3.3.1 v-on 監(jiān)聽事件
使用v-on來監(jiān)聽事件,這些事件包括HTML元素的DOM原生事件,也可能是組件的自定義事件,組件使用$emit()方法來觸發(fā)事件。v-on縮寫為@符號
<div id="app">
<!-- v-on指令接收javascript代碼 -->
<button v-on:click="counter += 1">點擊</button>
<!-- v-on語法糖形式 -->
<button @click="counter += 1">點擊</button>
<p>點擊了按鈕 {{ counter }} 次</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
counter: 0
}
})
</script>
事件的處理有3種方式,第1種為直接將js代碼加在v-on指令中,如上所示。當(dāng)代碼量較多時,這種方式不可行,此時可用第2種方式,將事件的處理邏輯封裝到方法中,把方法與事件綁定起來。
<div id="app">
<!-- v-on指令接收方法名 -->
<button @click="increment">點擊</button>
<p>點擊了按鈕 {{ counter }} 次</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
counter: 0
},
methods:{
increment(){
this.counter++;
}
}
})
</script>
第3種是使用內(nèi)斂語句調(diào)用方法,這種方式和第1種基本一樣,只是第1種是執(zhí)行語句,這里是執(zhí)行方法,這種方式很常用,它比第2種方式的優(yōu)勢在于,可以傳遞$event參數(shù)。Vue中使用變量$event表示原始DOM事件的Event 對象,拿到event對象后我們可以做很多事,比如獲取觸發(fā)事件的元素、鍵盤按鍵的狀態(tài)、鼠標(biāo)的位置、鼠標(biāo)按鈕的狀態(tài)等。
<div id="app">
<!-- v-on指令使用內(nèi)斂語句調(diào)用方法 -->
<button @click="sayHello('張三',$evnet)">點擊</button>
<p>{{ msg }} </p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
msg: "",
},
methods:{
sayHello(name,event){
if(evnet){
event.preventDefault();
}
this.msg = "Hello " + name;
}
}
})
</script>
下面列出一些常用的event事件標(biāo)準(zhǔn)屬性和方法,具體參考MDN DOM Event接口
| 屬性/方法 | 描述 |
|---|---|
| ctrlKey | 當(dāng)鼠標(biāo)事件觸發(fā)時,如果 control 鍵被按下,則返回 true; |
| altKey | 當(dāng)鼠標(biāo)事件觸發(fā)的時,如果alt 鍵被按下,返回true; |
| shiftKey | 當(dāng)鼠標(biāo)事件觸發(fā)時,如果 shift 鍵被按下,則返回 true; |
| button | 當(dāng)鼠標(biāo)事件觸發(fā)的時,如果鼠標(biāo)按鈕被按下,將會返回一個數(shù)值。 |
| clientX | 鼠標(biāo)指針在點擊元素(DOM)中的X坐標(biāo)。 |
| clientY | 鼠標(biāo)指針在點擊元素(DOM)中的Y坐標(biāo)。 |
| screenX | 鼠標(biāo)指針相對于全局(屏幕)的X坐標(biāo) |
| screenY | 鼠標(biāo)指針相對于全局(屏幕)的Y坐標(biāo) |
| bubbles | 表示該事件是否在DOM中冒泡。 |
| cancelable | 用來表示這個事件是否可以取消。 |
| currentTarget | 返回注冊這個事件監(jiān)聽的對象。 |
| eventPhase | 事件流正在處理哪個階段。 |
| target | 觸發(fā)該事件的元素。 |
| type | 返回當(dāng)前 Event 對象表示的事件的名稱,及事件的類型(不區(qū)分大小寫) |
| initEvent() | 初始化新創(chuàng)建的 Event 對象的屬性。 |
| preventDefault() | 取消事件,不執(zhí)行與事件關(guān)聯(lián)的默認動作。 |
| stopPropagation() | 停止事件冒泡。 |
v-on修飾符
Vue為v-on提供了事件修飾符,在一定程度上簡化了在方法中處理$event對象的邏輯。
-
.stop- 調(diào)用event.stopPropagation()。 -
.prevent- 調(diào)用event.preventDefault()。 -
.capture- 添加事件偵聽器時使用 capture 模式。 -
.self- 只當(dāng)事件是從偵聽器綁定的元素本身觸發(fā)時才觸發(fā)回調(diào)。 -
.{keyCode | keyAlias}- 只當(dāng)事件是從特定鍵觸發(fā)時才觸發(fā)回調(diào)。 -
.native- 監(jiān)聽組件根元素的原生事件。 -
.once- 只觸發(fā)一次回調(diào)。 -
.left- (2.2.0) 只當(dāng)點擊鼠標(biāo)左鍵時觸發(fā)。 -
.right- (2.2.0) 只當(dāng)點擊鼠標(biāo)右鍵時觸發(fā)。 -
.middle- (2.2.0) 只當(dāng)點擊鼠標(biāo)中鍵時觸發(fā)。 -
.passive- (2.3.0) 以{ passive: true }模式添加偵聽器
其中.capture表示該事件使用捕獲模式(默認是冒泡模式),我們在用js原生添加事件監(jiān)聽使用如下方法,event是事件名稱,如click;listener是為該事件注冊的方法,useCapture是使用捕獲模式還是冒泡模式。
addEventListener(event, listener, useCapture)
useCapture默認是false的,也就是說默認使用冒泡事件,關(guān)于捕獲和冒泡可以簡單的理解,捕獲模式是事件傳播的方向是由根元素向目標(biāo)前進,冒泡模式表示事件的傳播方向是由目標(biāo)元素向根元素前進。
3.3.2 v-bind 屬性綁定
將表達式動態(tài)綁定到HTML元素/組件的屬性上,縮寫為:符號
<div id="app">
<a v-bind:href="url">百度</a>
<!-- v-bind語法糖形式 -->
<a :href="url">百度</a>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
url:"www.baidu.com",
}
});
</script>
v-bind的寫法為v-bind:foo="bar",其中foo表示v-bind的參數(shù),代表的是原生/組件的屬性名,bar是預(yù)期值,即屬性對應(yīng)的值。
參數(shù)時可選的,也就是說,v-bind可指定屬性名也可不指定屬性名,當(dāng)不帶參數(shù)時,要綁定到一個包含鍵值對的預(yù)期值對象,對象的key就是參數(shù),對象key對應(yīng)的value就是參數(shù)對應(yīng)的值,如下:
<img v-bind="{src:'a.png',alt='呵呵',height:32,width:32}">
<!--等價于-->
<img src="a.png" alt="呵呵" height="32" width="32">
可以利用無參指令,一次性綁定多個屬性。
在向自定義組件綁定屬性值時(通過props傳遞),數(shù)據(jù)是單向流動的,父組件向子組件傳遞數(shù)據(jù),反之不行。常規(guī)的做法是,父組件通過子組件的props傳值給子組件,同時在子組件上注冊監(jiān)聽事件,子組件通過$emit方法觸發(fā)父組件的監(jiān)聽方法,修改props對應(yīng)的值。
Vue提供了.sync修飾符,它實際上是一個語法糖,加了.sync的prop,vue會自動的為其生成一個事件監(jiān)聽,監(jiān)聽的名稱為update:propName,如要子組件要修改prop的值,只需調(diào)用this.$emit('update:propName',value)即可,如下所示:
<body>
<div id="app">
<h3>{{msg}}</h3>
<!-- 引用組件,將msg賦值到組件的title屬性上,
并使用.sync修飾符(語法糖)添加v-on事件監(jiān)聽,
該事件名稱的格式為 update:propName,對應(yīng)到這里是update:title-->
<son :title.sync="msg" />
</div>
<!-- 使用x-template定義組件的HTML模板 -->
<script type="text/x-template" id="son">
<div>
<p>{{title}}</p>
<button @click="btnClicked">點我</button>
</div>
</script>
<script>
// 組件定義
Vue.component('son', {
//定義組件屬性
props:['title'],
//找到id為son的HTML模板
template: "#son",
methods: {
btnClicked() {
//觸發(fā)由.sync生成的upadte:title事件
this.$emit("update:title","子組件改變后的標(biāo)題")
}
},
});
var vm = new Vue({
el: "#app",
data: {
msg: "標(biāo)題"
}
});
</script>
</body>
注意:.sync修飾符后面不能接表達式。
3.3.3 class與style增強綁定
v-bind可以綁定元素的所有屬性,其中最常用的是元素的class和style屬性,不過當(dāng)這兩個屬性的邏輯較為復(fù)雜時,使用字符串的方式返回該屬性值體驗非常差,所以,Vue增強了對class和style的綁定。
class的綁定
使用v-bind綁定class有對象語法和數(shù)字語法兩種方式,在對象語法中key表示css的類名,value是個bool值,為true時,該key對應(yīng)的類名加入class中,為false時,對應(yīng)的項從class中移除。
<div class="cell" :class="{'active':isActive,'error':isError}"></div>
<script>
var vm = new Vue({
data:{
isActive:true,
isError:false,
}
});
</script>
上面的代碼最終生成的class為:
<div class="cell active"></div>
v-bind:class生成的動態(tài)綁定類會與class屬性中的類合并。這isActive的值為true,表示active會加入到class類中,isError為false,div的class中會移除error類。
數(shù)組語法就相對簡單,只要是數(shù)組中的字符串,都會加到class類中。在數(shù)組中可以使用三元表達式來動態(tài)的加載類。
<div :class="[cell,isActive?'active':'',isError?'error':'']"></div>
style內(nèi)聯(lián)樣式綁定
v-bind:sytle也有對象語法和數(shù)據(jù)語法,但是對象語法較為常見
<div :style="cellStyle"></div>
<script>
var vm = new Vue({
cellStyle:{
fontSize:14+'px',
color:'red'
}
});
</script>
結(jié)果為:
<div style="color:red;font-size:14px;"></div>
3.3.4 v-model雙向綁定
v-model指令用于在表單類元素上實現(xiàn)數(shù)據(jù)的雙向綁定,它的本質(zhì)也是監(jiān)聽表單類元素的輸入事件,更新綁定的數(shù)據(jù),vue對以下列表單元素做了處理。
<!-- 輸入框元素,綁定value屬性,監(jiān)聽input事件 -->
<input type="text" v-model="text">
<!-- 文本域元素,綁定value屬性,監(jiān)聽input事件 -->
<textarea v-model="text"></textarea>
<!-- 單個復(fù)選框時,v-model綁定一個bool值,該值指示checkbox添加/移除checked屬性,監(jiān)聽change事件 -->
<input type="checkbox" v-model="checked" id="checkbox1">
<label for="cbox1">{{checkd}}</label>
<!--
多個復(fù)選框時,v-model綁定一個數(shù)組,與value屬性配合使用,監(jiān)聽change事件。
選中復(fù)選框時,將value值push到數(shù)組中,否則,將value值從數(shù)組中移除。
-->
<input type="checkbox" id="jack" value="jack" v-model="names">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="john" v-model="names">
<label for="john">John</label>
<!-- 單選按鈕,v-model綁定字符串,與value值配合使用-->
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<!--
選擇框,v-model綁定value值,監(jiān)聽change事件,也分為單選和多選,
因為實際使用select標(biāo)簽較少(樣式不統(tǒng)一),這里不在演示
-->
修飾符
-
.lazy修飾符適用于輸入框,設(shè)置后該修飾符后,v-model不再監(jiān)聽input事件,轉(zhuǎn)而監(jiān)聽change事件。也就是當(dāng)用戶輸入文本時,數(shù)據(jù)不是事實的同步,而是在文本框失去焦點或回車時更新。<input type="text" v-model.lazy="text"> .number將輸入框的內(nèi)容轉(zhuǎn)換為數(shù)字類型.trim自動過濾用戶輸入的首尾空白字符
3.3.5 自定義組件的v-model
v-model指令不僅可以在表單元素上實現(xiàn)數(shù)據(jù)的雙向綁定,還可以在自定義組件上實現(xiàn)雙向綁定。上面說了,v-model其實是語法糖,它的本質(zhì)是綁定值到元素的指定屬性上,并監(jiān)聽元素的輸入事件,vue在表單元素中為我們實現(xiàn)了該功能,而在自定義組件上,這個功能需要我們自己實現(xiàn)。
v-model在自定義組件上默認綁定value屬性,監(jiān)聽input事件。
<body>
<div id="app">
<h3>{{count}}</h3>
<!-- 在自定義組件上使用v-model指令 -->
<my-component v-model="count" />
</div>
<script type="text/x-template" id="myComponent">
<div>
<p>{{value}}</p>
<button @click="btnClicked">點我</button>
</div>
</script>
<script>
// 組件定義
Vue.component('myComponent', {
//組件中定義value屬性
props:["value"],
template: "#myComponent",
methods: {
btnClicked() {
// 發(fā)送input事件
this.$emit("input",this.value+1);
}
},
});
var vm = new Vue({
el: "#app",
data: {
count: 0,
},
});
</script>
</body>
因為v-model默認綁定的是value屬性,所以需要在組件中定義一個value。v-model指令默認監(jiān)聽input事件(自動生成),所以,執(zhí)行通過$emit觸發(fā)input事假即可。
如果希望改變v-model在組件上默認的屬性和事件,可以使用model選項,model選項接收一個對象,這個對象有兩個屬性:prop和event。prop的值默認是value,event的值默認是input。
<script>
Vue.component('myComponent',{
model:{
//修改v-model的默認綁定的value屬性和input監(jiān)聽事件
prop:"count",
event:"countChange"
},
props:["count"],
template:"#myComponent",
methods:{
btnClicked(){
this.$emit("contChange",this.count+1);
}
}
});
</script>