Vue(四) 內建指令
現在介紹 Vue.js 中 更多的內建指令
基本指令
v-cloak
v-cloak 不需要表示式,它會在 Vue 例項結束編譯時從繫結的 HTML 元素上移除,經常和 CSS 的 display: none; 配合使用:
<div id="app" v-cloak> {{ message }} </div> <script> var app = new Vue({ el: '#app', data: { message: '這是一段文字' } }) </script>
這時雖然已經加了指令,但其實沒有起到任何作用,Vue.js 檔案還沒載入完時,在頁面上會顯示 {{ message }} 的字樣,直到 Vue 建立例項、編譯模板時,DOM才會被替換,所以這個過程螢幕是有閃動的。只要加一句 CSS 就可以解決問題了:
[v-cloak] {
display: none;
}
一般情況下,v-cloak 是一個解決初始化慢導致頁面閃動的最佳實踐,對於簡單的專案很實用,但在具有工程化的專案裡,專案的 HTML 結構只有一個空的 div 元素,剩餘的內容都由路由去掛載不同的元件完成,所以不再需要 v-cloak。
v-once
v-once 也是一個不需要表示式的指令,作用是定義它的元素或元件只渲染一次,包括元素或元件的所有子節點。首次渲染後,不再隨資料的變化重新渲染,將被視為靜態內容,例如:
<div id="app">
<div v-once>{{ message }}</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: '這是一段文字'
}
})
</script>
v-once 在業務中很少使用,當你需要進一步優化效能時,可能會用到。
條件渲染指令
v-if、v-else-if、v-else
與 JavaScript 的條件語句 if、else、else if 類似,Vue.js 的條件命令可以根據表示式的值在 DOM 中渲染或銷燬元素的元件,例如:
<div id="app">
<p v-if="status === 1">當status === 1,顯示改行</p>
<p v-else-if="status === 2">當status === 2,顯示改行</p>
<p v-else>否則顯示改行</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
status: 3
}
})
</script>
v-else -if 需要緊跟 v-if,v-else 需要緊跟 v-else-if 或 v-if,表示式的值為真時,當前元素/元件及所有子節點將被渲染,為假時被移除。
如果一次判斷的是多個元素,可以在 Vue.js 內建的<template>元素上使用條件指令,最終渲染的結果不會包含該元素,例如:
<div id="app">
<template v-if="status === 1">
<p>這是一段文字</p>
<p>這是一段文字</p>
<p>這是一段文字</p>
</template>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
status: 1
}
})
</script>
Vue 在渲染元素時,出於效率考慮,會盡可能地複用已有的元素而非重新渲染,例如:
<div id="app">
<template v-if="type === 'name'">
<label>使用者名稱:</label>
<input placeholder="輸入使用者名稱">
</template>
<template v-else>
<label>郵箱:</label>
<input placeholder="輸入郵箱">
</template>
<button @click="change">點我切換</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
type: 'name'
},
methods: {
change: function () {
this.type = this.type === 'name' ? 'mail' : 'name';
}
}
})
</script>
點選切換按鈕,雖然 DOM 變了,但是在輸入框鍵入的內容並沒有改變,只是替換了 placeholder 的內容,說明 <input> 元素被複用了。
如果你不希望這麼做,可以使用 Vue.js 提供的 key 屬性,它可以讓你決定是否要複用元素,key 的值必須是唯一的,例如:
div id="app">
<template v-if="type === 'name'">
<label>使用者名稱:</label>
<input placeholder="輸入使用者名稱" key="name-input">
</template>
<template v-else>
<label>郵箱:</label>
<input placeholder="輸入郵箱" key="mail-input">
</template>
<button @click="change">點我切換</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
type: 'name'
},
methods: {
change: function () {
this.type = this.type === 'name' ? 'mail' : 'name';
}
}
})
</script>
給兩個 <input> 元素都增加 key 後,就不會被複用了,切換時鍵入的內容也會被刪除,不過<lable> 元素仍然是被複用的,因為沒有新增 key 屬性。
v-show
v-show 的用法與 v-if 基本一致,只不過 v-show 是改變元素的 CSS 屬性 display。當 v-show 表示式的值為 false 時,元素會被隱藏,檢視 DOM 結構會看到元素上載入了內聯樣式 display: none; ,例如:
<div id="app">
<p v-show="status === 1">當 status 為 1 時顯示改行</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
status: 2
}
})
</script>
渲染後的結果為:
<p style="display: none;">當 status 為 1 時顯示改行</p>
v-show 不能在 <template> 上使用
v-if 與 v-show 的選擇
v-if 與 v-show 具有類似的功能,不過 v-if 才是真正的條件渲染,它會根據表示式適當地銷燬或重建元素及繫結的事件或子元件。若表示式初始值為 false,則一開始元素/元件並不會渲染,只有當條件第一次變為真時才開始編譯。
而 v-show 只是簡單的 CSS 屬性切換,無論條件真與否,都會被編譯。因此,v-if 更適合條件不經常改變的場景,因為它切換開銷相對較大,而 v-show 適用於頻繁切換條件的常見。
列表渲染指令 v-for
基本用法
v-for 可以將一個數組遍歷或列舉一個物件迴圈顯示。它的表示式需結合 in 來使用,類似 item in items 的形式,舉例:
<div id="app">
<ul>
<li v-for="book in books">{{ book.name }}</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
books: [
{ name: 'Java 程式設計思想'},
{ name: 'Java 核心技術'},
{ name: '深入理解 Java 虛擬機器'}
]
}
})
</script>
除了陣列的屬性,物件的屬性也是可以遍歷的,例如:
<div id="app">
<span v-for="value in user">{{ value }} </span>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
user: {
name: 'kindleheart',
age: 20,
gender: '男'
}
}
})
</script>
遍歷物件屬性時,有兩個可選引數,分別是鍵名和索引:
<div id="app">
<ul>
<li v-for="(value, key, index) in user">
{{ value }} - {{ key }} - {{ index }}
</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
user: {
name: 'kindleheart',
age: 20,
gender: '男'
}
}
})
</script>
v-for 還可以迭代整數:
<div id="app">
<span v-for="n in 10">{{ n }} </span>
</div>
<script>
var app = new Vue({
el: '#app',
})
</script>
陣列更新
Vue 的核心是資料與檢視的雙向繫結,當我們修改陣列時,Vue會檢測到資料變化,所以用 v-for 渲染的檢視也會立即更新。Vue 包含了一組觀察陣列變化的方法,使用它們改變陣列也會觸發檢視更新:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
使用以上方法會改變被這些方法呼叫的原生陣列,有些方法不會改變原陣列,例如:
- filter()
- concat()
- slice()
它們返回的是一個新陣列,下面是找出含有 Java 關鍵字書籍的例子,例如:
<div id="app">
<ul>
<template v-for="book in books">
<li>書名:{{ book.name }}</li>
<li>價格:{{ book.price }}</li>
</template>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
books: [
{
name: 'Java 程式設計思想',
price: 100
},
{
name: 'Java 核心技術',
price: 200
},
{
name: 'JS 高階程式設計',
price: 300
}
]
}
});
app.books = app.books.filter(function (item) {
return item.name.match(/Java/);
})
</script>
Vue 在檢測到陣列變化時,並不是重新渲染整個列表,而是最大化地複用 DOM 元素。替換的陣列中,含有相同元素的項不會被重新渲染,因此可以大膽地用新陣列來替換舊陣列,不用擔心效能問題。
需要注意的是,以下變動的陣列中,Vue 是不能檢測到的,也不會觸發檢視更新:
- 通過索引直接設定項、比如 app.books[3] = {...}。
- 修改陣列長度,比如 app.books.length = 1。
解決第一個問題可以用兩種方法實現同樣的效果,第一張是Vue內建的 set 方法:
Vue.set(app.books, 3, {
name: '<<CSS 揭祕>>',
price: 150
});
在 webpack 元件化的方式,預設是沒有匯入 Vue 的,這時可以使用 $set,例如:
//這裡的 this 指向的是當前元件例項
this.$set(app.books, 3, {
name: '<<CSS 揭祕>>',
price: 150
});
還可以使用 app.$set(...) 的方式。
第二個問題可以使用 splice 類解決:
app.books.splice(1);
過濾與排序
當你不想改變原陣列,想通過一個數組的副本來做過濾或排序的顯示時,可以使用計算屬性來返回過濾或排序後的陣列,例如:
<div id="app">
<ul>
<template v-for="book in filterBooks">
<li>書名:{{ book.name }}</li>
<li>價格:{{ book.price }}</li>
</template>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
books: [
{
name: 'Java 程式設計思想',
price: 100
},
{
name: 'Java 核心技術',
price: 200
},
{
name: 'JS 高階程式設計',
price: 300
}
]
},
computed: {
filterBooks: function () {
return this.books.filter(function (book) {
return book.name.match(/Java/);
});
}
}
});
</script>
方法與事件
基本用法
在之前我們已經瞭解了 Vue.js 通過 v-on 指令監聽事件,v-on 指令的表示式可以是一個 JavaScript 語句還可以是一個在 Vue 例項中 methods 選項內的函式名,例如:
<div id="app">
點選次數:{{ count }}
<button @click="count++">+1</button>
<button @click="add()">+1</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add: function () {
this.count++;
}
}
});
</script>
需要注意的是 @click 呼叫的方法名後可以不加括號 "()" ,在大部分業務場景中,如果不需要傳入引數,為了簡便可以不寫括號。
Vue 提供了一個特殊變數 $event,用於訪問原生 DOM 事件,例如下面的例項可以阻止連結開啟:
<div id="app">
<a href="http://www.apple.com" @click="handleClick('禁止開啟', $event)">開啟連結</a>
</div>
<script>
var app = new Vue({
el: '#app',
methods: {
handleClick: function (message, event) {
event.preventDefault();
window.alert(message);
}
}
});
</script>
修飾符
在上例使用的 event.preventDefault() 也可以使用 Vue 事件的修飾符來時實現,在@繫結的事件後加小圓點 ".",再跟一個字尾使用修飾符。Vue 支援以下修飾符:
- .stop
- .prevent
- .capture
- .self
- .once
- .passive
<!-- 阻止單擊事件繼續傳播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再過載頁面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修飾符可以串聯 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修飾符 -->
<form v-on:submit.prevent></form>
<!-- 新增事件監聽器時使用事件捕獲模式 -->
<!-- 即元素自身觸發的事件先在此處理,然後才交由內部元素進行處理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 點選事件將只會觸發一次 -->
<a v-on:click.once="doThis"></a>
在表單元素上監聽鍵盤事件,還可以使用按鍵修飾符,例如:
<!-- 只有在 keyCode 是 13 時呼叫 submit() 方法 -->
<input @keyup.13="submit">
也可以自己配置具體按鍵:
Vue.config.keyCodes.f1 = 112;
// 全域性定義後,就可以使用@keyup.f1
除了具體的某個 keyCode 外,Vue 還提供了一些快捷名稱,以下是全部的別名:
- .enter
- .tab
- .delete (捕獲“刪除”和“退格”鍵)
- .esc
- .space
- .up
- .down
- .left
- .right
這些修飾符也可以組合使用,或和滑鼠一起配合使用:
- .ctrl
- .alt
- .shift
- .meta(Mac 下是 Command 鍵,在 Windows 下是視窗鍵)
例如:
<!-- Shift + S -->
<input @ketup.shift.83="handleSave">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>