by yugasun from yugasun.com/post/you-ma… 本文可全文轉載,但需要保留原作者和出處。
在
Vuejs
中,指令(Directives)是帶有v-
字首的特殊屬性。指令屬性的預期值是 單個 Javascript 表示式(v-for
是個例外)。指令的職責是,當表示式改變時,將其產生的連帶影響,響應式的作用於 DOM。
當然,Vue 除了核心功能預設內建的指令外,更強大的是它允許註冊自定義指令,這是個讓我非常驚喜功能。因為當初在使用 Angular1.x
時就特別喜好自定義指令這個功能,沒想到 Vue 也借鑑了進來,讓我能夠靈活對 DOM 進行底層操作,而且它具有非常高的複用性,書寫起來也非常簡潔。
當然在 Vue 中,程式碼的複用和抽象的主要形式還是 元件
,這將在下一節中講到。
內建指令
Vue 提供供了大概 13 種內建指令,基本上能夠滿足我們的日常開發需求,不得不說作者考慮的已經非常全面,給尤大大點贊。這裡只介紹 v-for、v-on 兩種內建指令,感興趣的可以到 官方API 瞭解。
1.v-for
上一節我們已經使用過了,它是基於源資料多次渲染元素或莫板塊的。此指令的值,必須使用特定語法 alias in expression
來進行輸出。 expression
為需要遍歷的物件,它可以是 Array | Object | number | string
四種型別,alias
為遍歷的元素別名。這個跟 js 的遍歷函式 map
很相似,先來看個例子:
<div id="app1">
<ul>
<li v-for="item in 5">
{{ item }}
</li>
</ul>
</div>
複製程式碼
var app1 = new Vue({
el: "#app1",
})
複製程式碼
這裡是在頁面上依次輸出 1~5 的數字,此時 expression
為 5
,number
(number 必須為正整數) 型別。 Vue 在渲染時,會先建立一個長度為 5
的陣列,然後遍歷輸出 item + 1
的值。如果為 string
型別,就會建立一個該字串長度的陣列,再遍歷輸出字串的每個字元。單純講肯定不太好理解,其實這部分原始碼很簡單,大家可以直接通過閱讀來理解:官方原始碼。
大多數時候,expression
都只是個陣列,其實知道它還可以為 string
或者 number
是很有用的,比如我們需要快速重複輸出一個DOM,使用 number
能很快實現,節約了我們複製貼上或者書寫陣列列表的時間。當為 string
時,我們可以根據需求依次輸出每個字元,以此可以做出很炫酷的打字效果,如下:
<div id="app2">
<span v-for="(item, index) in name" v-bind:style="{animationDelay: `${0.5 + index * 0.3}s`}">{{ item }}</span>
</div>
複製程式碼
#app2 span {
animation: flip-in 1s 0s ease-in-out both;
}
@keyframes flip-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
複製程式碼
// demo 2
var app2 = new Vue({
el: "#app2",
data () {
return {
name: 'yugasun'
}
}
})
複製程式碼
怎麼樣?曾今看到別人做出炫酷的打字效果簡歷是不是很羨慕,這裡只需要20行程式碼就可以實現打字效果,趕緊動手試試吧~
注意: 這裡在設定每個字元的
animation-delay
屬性時,用到了v-bind
指令,這個很簡單,這裡不做贅述。同時使用了 ES6 的模板字元語法來計算時延的值,如果瀏覽器不支援,建議更換為新版 Chrome 瀏覽器。模板字串 教程在這裡。
2.v-on
v-on
指令是用來繫結事件監聽器的,事件型別可以是DOM原生事件,也可以是通過 $emit 觸發的自定義事件。但是隻有用在自定義元素元件上時,才可以監聽元件觸發的自定義事件。
在監聽原生 DOM 事件時,方法以事件為唯一的引數。如果使用內聯語句,語句可以訪問一個
$event
屬性:v-on:click="handle('ok', $event)"
看程式碼更好理解:
<div id="app3">
<button v-on:click="handleClick('確定', $event)">確定</button>
<button v-on:click="handleClick('取消', $event)">取消</button><br>
{{ msg }}
</div>
複製程式碼
var app3 = new Vue({
el: "#app3",
data () {
return {
btnText: ''
}
},
computed: {
msg () {
return this.btnText ? `點選了 ${this.btnText} 按鈕` : '您還沒點選任何按鈕'
}
},
methods: {
handleClick (text, e) {
console.log(e)
this.btnText = text
}
}
})
複製程式碼
這裡通過 v-on
監聽了兩個按鈕的點選事件,並分別傳遞了各自的文字到監聽函式進行輸出,輸出的 msg
是計算屬性(如果不懂什麼是計算屬性,教程在這裡 深入淺出響應式系統),依賴 btnText
動態計算的。可以通過控制檯檢視列印出的引數 e
物件就是一個原生 MouseEvent
物件。
除了基本的用法, v-on
指令還提供了豐富的 修飾符
來配合使用,這樣可以寫出非常靈活的監聽事件,比如我們經常使用的 e.preventDefault()
,在 Vue 中我們只需要通過 v-on:click.prevent
就可以實現了,是不是很炫酷,所有的修飾符列表在 這裡。這裡我們來演示下,點選滑鼠左鍵和右鍵的事件監聽,將上面的 html 程式碼修改如下:
<div id="app3">
<!-- 相對於上面只新增了 `.left` 和 `.right` 修飾符 -->
<button v-on:click.left="handleClick('確定', $event)">確定</button>
<button v-on:click.right="handleClick('取消', $event)">取消</button><br>
{{ msg }}
</div>
複製程式碼
自定義指令
最讓我激動人心的就是自定義指令了,它即可以通過 Vue.directive 註冊全域性指令,也可以給 Vue 例項新增 directives
屬性來註冊區域性指令。這兩種指令都是通過指令定義時的鉤子函式實現的。
關於鉤子函式和鉤子函式引數介紹請直接閱讀官方文件,官方文件已經講述的非常詳細了,這裡不再贅述,直通車:鉤子函式、鉤子函式引數
相信你們已經很快看完了官方文件介紹了,好了現在我將帶你來領略自定義指令的強大,實現一個倒數計時指令。
鉤子函式 bind
是在元素第一次繫結到元素時呼叫的,所以我們可以在這裡初始化繫結的元素 el
,而且繫結元素已經作為所有鉤子函式的第一個引數被傳入,這裡可以通過 el.innerHTML
就很容易修改繫結元素的內容了,程式碼如下:
<div id="app4">
<button v-on:click="startCount">開始倒數計時</button>
<span data-count="60" v-count-down="count" v-if="show" style="font-size:16px;"></span>
</div>
複製程式碼
var app4 = new Vue({
el: '#app4',
data () {
return {
show: false, // 是否顯示倒數計時
timer: null // 定時器
}
},
methods: {
startCount: function () {
this.show = true
}
},
directives: {
'count-down': {
bind: function (el, binding, vnode) {
el.innerHTML = '60'
}
}
}
})
複製程式碼
當我們點選按鈕是,將 show
置為 true
,此時 v-if
指令就判斷顯示使用 v-count-down
指令的元素,然後執行 bind
初始化函式,將元素內容修改為 60
。這裡很好理解。但是我們要實現倒數計時,就需要建立一個定時器,並需要維護一個數實現遞減,這裡通過動態修改 data-count
的值來實現。
實現思路:當初始化時,建立一個定時器,每隔1s將獲取
data-count
的值減一,然後賦給el.innerHTML
,同時修改data-count
屬性為新的值,當然還需要新增判斷,就是當然data-count
為0時,清除計時器。
有了思路,實現程式碼就非常簡單了:
<div id="app4">
<button v-on:click="startCount">開始倒數計時</button>
<span v-count-down data-count="60" v-if="show" style="font-size:16px;">
60
</span>
</div>
複製程式碼
var app4 = new Vue({
el: '#app4',
data () {
return {
show: false, // 是否顯示倒數計時
}
},
methods: {
startCount: function () {
this.show = true
}
},
directives: {
'count-down': {
bind: function (el, binding, vnode) {
let count = parseInt(el.getAttribute('data-count'))
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
// 大家可以推測下這裡的 this 指什麼?
this.timer = setInterval(function () {
if (count <= 0) {
clearInterval(this.timer)
this.timer = null
} else {
count--
el.innerHTML = count
el.setAttribute('data-count', count)
}
}, 1000)
}
}
}
})
複製程式碼
指令系統就介紹到這裡,最後留給大家一個問題:
上面程式碼中有個
this.timer
, 那麼這個this
指的是什麼呢?如果我們需要將這個timer
和count
變數 維護在當前 Vue 例項中,該如何做呢?