1. 程式人生 > >Vue(四) 內建指令

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>