本文主要講述的是Vue自定義指令內容,爲後續實現級聯組件做鋪墊。vue
首先以經常使用的v-model指令的使用爲例:node
<input type="text" v-model.lazy="age"/>{{age}}
Vue指令主要用於 須要進行一些DOM操做的時候,經過Vue指令 將DOM操做進行封裝。
使用指令的時候分爲 左右兩部分,等號左邊是 指令名(v-model)和 修飾符(.lazy),等號右邊是 指令表達式(age),指令表達式雖然是一個單純的字符串,可是指令表達式的值倒是 當前組件上的同名屬性對應的值,上面input輸入框中顯示的將是 this.age的值
這裏補充一下v-model指令的三個修飾符的用法:express
<input type="text" v-model.number="age"/>{{age}}
當用戶在輸入框中輸入"18a"後,最右側顯示的age的值 仍然爲18,由於其會將"18a"先轉換爲數字類型,因此值仍然爲 數字18;當用戶輸入"a18"後,最右側顯示的age值就會變爲"a18",由於"a18"沒法轉換爲數字,因此仍然顯示 字符串"a18"
指令 實際上是一個對象。若是想在Vue組件中建立自定義的指令,那麼能夠在組件上添加 directives屬性,其 屬性值爲一個對象,而後將自定義指令註冊到directives屬性值上,對象的 屬性名爲 指令名稱,對象的 屬性值爲 自定義指令對象。須要注意的是,指令名稱採用的駝峯命名法, 不包括v-部分,使用的時候 駝峯命名大寫部分轉換爲小寫並用-隔開,如:
// 某個組件ide
export default { directives: { // 在當前組件上註冊一個指令 clickOutside: { // 指令名稱爲clickOutside,使用的時候使用v-click-outside // 指令對象 } } }
<!--使用指令--> <div v-click-outside:bar.foo="close"></div>
指令對象內部主要是一些鉤子函數,如: bind、 inserted、 update、 componentUpdated、 unbind,比較經常使用的是inserted, 在使用了該指令的元素被插入到父元素內時候執行,固然最重要的就是傳遞給鉤子函數處理的參數,由於指令最重要的做用就是 操做DOM,因此其
① 第一個參數就是el,是綁定了指令的DOM元素對象,若是綁定指令的是一個組件,也就是說指令用在了組件上,那麼el就表明這個組件渲染後的整個DOM元素對象。函數
② 第二個參數就是binding,是一個對象,包含了當前指令的一些屬性,好比: binding.name值爲click-outside,不包含v-部分,binding.expression值爲close,binding.value值就是this.close的值,即當前組件上close屬性對應的值,能夠是一個單純的數據,也能夠是一個方法名,這裏close就是一個組件上的close()方法,binding. modifiers值爲{foo:true}就是當前指令的修飾符,是一個對象,若是沒有修飾符則是一個空的對象{},binding.arg值爲bar,是傳遞給指令的參數,即冒號修飾的部分this
注意: 指令的參數必須寫在修飾符的前面,即冒號必須在點號前。雙向綁定
③第三個參數爲vnode,即Vue編譯生成的虛擬節點,這個參數很是有用,能夠獲取到很是多Vue實例相關的東西,vnode.context能夠獲取到當前vue組件實例,vnode.componentInstance,若是指令是用在某個組件上,那麼componentInstance獲取的就是使用了該指令的那個Vue組件實例code
export default { directives: { // 在當前組件上註冊一個指令 clickOutside: { // 指令名稱爲clickOutside,使用的時候使用v-click-outside inserted(el, binding) { document.addEventListener("click", (e) => { // 給整個document添加click事件 if (e.target === el || el.contains(e.target)) { // 若是點擊的區域是使用了該指令的DOM元素的內部,那麼不作任何處理 return; } binding.value(); // 即調用close()方法 }); } } } }
該指令實現的是點擊了 綁定指令的元素的外面後,執行指令綁定的方法
實現一個 v-my-model指令,具備v-model的大部分功能,如下指令代碼並非源碼的實現,僅僅是簡單模擬一下v-model指令的功能,包括v-model的雙向綁定功能、lazy修飾符、number修飾符、trim修飾符。
export default { directives: { // 註冊v-my-model到組件上 myModel: { // 指令名稱採用駝峯命名法 inserted(el, binding, vnode) { // 在綁定元素插入父元素後執行 el.value = binding.value; // 獲取指令表達式的值做爲input元素的value值 const eventName = binding.modifiers.lazy ? "change": "input"; // 處理lazy修飾符,若是有lazy屬性則換成change事件 el.addEventListener(eventName, (e) => { // input元素監聽input或者change事件 let result = e.target.value; // 獲取用戶輸入 if (binding.modifiers.number && !Number.isNaN(parseInt(e.target.value))) { // 若是指令帶有number修飾符,而且用戶的輸入可以轉換爲number result = parseInt(e.target.value); // 將用戶的輸入轉換爲對應的數字 } if (binding.modifiers.trim) { // 若是指令帶有trim修飾符 result = result.trim(); // 去除用戶輸入的首尾空格 } vnode.context[binding.expression] = result; // 將最終的結果保存到當前組件實例上即view --> model中 }); vnode.context.$watch(binding.expression, (newValue, oldValue) => { // 獲取組件實例並監聽其中的綁定的值變化 el.value = newValue; // 若是組件實例上數據發生變化,那麼更新view數據 }); } } } }
v-my-model指令主要就是監聽 input或者 change事件,同時經過 監聽組件實例中指定數據的變化實現數據的 雙向綁定。大致上實現了v-model指令的功能。