在講解視圖層指令時,咱們講到ref
特性,使用它咱們能夠獲取當前DOM
元素對象,以便執行相關操做。html
<div id="app"> <input ref="ipt" type="text" v-model="value" /> </div>
new Vue({ el: "#app", data: { value: '' }, methods: { handleEle() { let ele = this.$refs.ipt // do somthing } } })
若是某個DOM
操做在不一樣組件的元素或組件內多個元素都須要執行,像這樣在每一個組件裏都重複寫一遍處理邏輯代碼確定不是好辦法。此時咱們能夠自定義一個指令,在指令鉤子函數的回調裏複用邏輯代碼。vue
其時,自定義指令的思想仍是代碼複用的想法,同組件、混入、函數思想同樣。node
根據指令使用範圍的不一樣,你能夠將指令定義在全局做用域、實例做用域或單個組件做用域內。express
// 在vue全局做用域 vue.directive('name', { bind: function (el, binding,vnode){/* do something */}, inserted: function (el, binding,vnode){/* do something */}, update: function (el, binding,vnode, oldVnode){/* do something */}, componentUpdated: function (el, binding,vnode, oldVnode){/* do something */}, unbind: function (el, binding,vnode){/* do something */}, }) // 在實例做用域中 var vm = new Vue({ directives: { 'name': { bind: function (el, binding,vnode){/* do something */}, inserted: function (el, binding,vnode){/* do something */}, update: function (el, binding,vnode, oldVnode){/* do something */}, componentUpdated: function (el, binding,vnode, oldVnode){/* do something */}, unbind: function (el, binding,vnode){/* do something */}, } } }) // 在組件做用域中 export default { directives: { 'name': { bind: function (el, binding,vnode){/* do something */}, inserted: function (el, binding,vnode){/* do something */}, update: function (el, binding,vnode, oldVnode){/* do something */}, componentUpdated: function (el, binding,vnode, oldVnode){/* do something */}, unbind: function (el, binding,vnode){/* do something */}, } } }
其中,指令有多個鉤子函數,正如vue
實例和組件有生命週期鉤子函數同樣。它們有不一樣的命名,和不一樣觸發時機。api
bind
: 鉤子函數會在指令綁定到元素時調用。只調用一次,指令第一次綁定到元素時調用。在這裏能夠進行一次性的初始化設置。inserted
:鉤子函數會在綁定的元素被添加到父節點時調用。可是和mounted
同樣,此進還不能保證元素已經被添加到父節點上(僅保證父節點存在)。可使用this.$nextTick
來保證這一點。update
:鉤子函數在綁定該指令的組件vnode
節點被更新時調用,可是該組件的子組件的vnode
可能此時還未更新。可是你能夠經過比較更新先後的值(vnode
, oldVnode
)來忽略沒必要要的模板更新 (詳細的鉤子函數參數見下)componentUpdated
:鉤子函數會在組件的 VNode 及其子組件的 VNode 所有更新後調用。unbind
:鉤子函數用於指令的拆除,只調用一次,當指令從元素上解綁時調用。但不是每次自定義指令的時候須要調用全部鉤子,事實上,它們都是可選的,你能夠根據自定義指令的需求肯定指令回調函數執行的時機,來綁定特定的時機的鉤子上。基本最經常使用的就是bind
和update
。針對最經常使用的有簡寫的方式,直接傳入一函數,但不建議,代碼中最好明確指令綁定的鉤子。數組
五個鉤子的回調函數都包含3個參數el
、binding
、vnode
。其中update
和componentUpdate
鉤子函數包含第4個參數oldVnode
。app
vue.directive('name', { bind: function (el, binding, vnode){/* do something */}, inserted: function (el, binding, vnode){/* do something */}, update: function (el, binding, vnode, oldVnode){/* do something */}, componentUpdated: function (el, binding, vnode, oldVnode){/* do something */}, unbind: function (el, binding, vnode){/* do something */}, })
咱們經過一個自定義指令例子來理解每一個參數具體的含義:
指令的使用都須要加上v
,而且以-
連字符鏈接。如v-name
、v-my-name
。async
<div v-my-directive:example.one.two = 'someExpression'>自定義指令示例<div>
// 全局註冊 vue.directive('my-directive', { bind: function (el, binding, vnode){/* do something */}, })
el
:指令所綁定的DOM
元素,能夠用來直接操做DOM
對象。vnode
: Vue
編譯生成的虛擬節點,具體API
見下文。
vnode.context
來獲取當前指令所處的上下文對象this
,這個屬性頗有用,vnode.ele
來獲取該節點元素,但必須在vnode.context.$nextTick(() => console.log(vnode.elm.parentNode))
獲得保證。oldVnode
:上一個虛擬節點,僅在 update
和 componentUpdated
鉤子中可用。binding
:對象,包含如下該指令的相關屬性。具體以下:
name
:指令的名稱,但不包含v-
部分,在上例中值爲my-directive
。arg
:指令的參數,即example
。modifiers
: 對象,指令的修飾符對象。即{one:true, two:true}
。expression
: 指令表達式的字符串形式。即"someExpression"
。value
: 指令表達式計算的結果,即value = someExpression
。若是data
對象中。{someExpression: (3/4).toFixed(2)
,則value = o.75
。oldVlue
:指令上一次的value
值。它只在update
和 componentUpdated
鉤子中可用。通常咱們使用指令,也就是由於有頻繁操做,因此在項目中通常都是全局註冊。在項目src
文件中定義一個directives
文件夾,新建一個index.js
函數
import Vue from 'vue' var hasPerm = { bind(el, binding, vnode){ let permissionList = vnode.context.$route.meta.permission if(!permissionList){ console.error(`權限判斷不生效。因路由中不包含permission字段,請檢查路由表設置。當前路由地址:${vnode.context.$route.path}`) return } if(typeof (permissionList) != "object" || !permissionList.length){ console.error(`權限判斷不生效。因路由中permission字段非數組類型或內容爲空,請檢查路由表設置。當前路由地址:${vnode.context.$route.path}`) return } if(!permissionList.includes(binding.value)){ el.parentNode.removeChild(el) } } } var directives = { hasPerm } for (item in directives) { Vue.directive(item, directives[item]) } export default Vue
// 在main.js文件導入 import './filters/install'
或者將自定義指令寫成插件形式,註冊到Vue
中this
const hasPermission = { install: function (Vue){ Vue.directive('hasPermission', { bind(el, binding, vnode){ let permissionList = vnode.context.$route.meta.permission if(!permissionList){ console.error(`權限判斷不生效。因路由中不包含permission字段,請檢查路由表設置。當前路由地址:${vnode.context.$route.path}`) return } if(typeof (permissionList) != "object" || !permissionList.length){ console.error(`權限判斷不生效。因路由中permission字段非數組類型或內容爲空,請檢查路由表設置。當前路由地址:${vnode.context.$route.path}`) return } if(!permissionList.includes(binding.value)){ el.parentNode.removeChild(el) } } }) } } export default hasPermission
// 全部自定義的插件都在plugins文件夾的install.js完成註冊 import Vue from 'vue' import hasPermission from './hasPermission' const Plugins = [ hasPermission ] // 全局註冊插件 Plugins.map((plugin) => { Vue.use(plugin) }) export default Vue
// 在main.js文件導入 import './plugins/install'
vnode API
vnode
參數
// 源碼路徑: vue/scr/vnode/vnode.js export default class VNode { // outer tag: string | void; data: VNodeData | void; children: ?Array<VNode>; text: string | void; elm: Node | void; ns: string | void; context: Component | void; // rendered in this component's scope key: string | number | void; componentOptions: VNodeComponentOptions | void; componentInstance: Component | void; // component instance parent: VNode | void; // component placeholder node // strictly internal raw: boolean; // contains raw HTML? (server only) isStatic: boolean; // hoisted static node isRootInsert: boolean; // necessary for enter transition check isComment: boolean; // empty comment placeholder? isCloned: boolean; // is a cloned node? isOnce: boolean; // is a v-once node? asyncFactory: Function | void; // async component factory function asyncMeta: Object | void; isAsyncPlaceholder: boolean; ssrContext: Object | void; fnContext: Component | void; // real context vm for functional nodes fnOptions: ?ComponentOptions; // for SSR caching devtoolsMeta: ?Object; // used to store functional render context for devtools fnScopeId: ?string; // functional scope id support // ... 其它代碼 }