vue自定義指令VNode詳解(轉)

一、自定義指令鉤子函數node

Vue.directive('my-directive', {
bind: function () {
// 作綁定的準備工做
// 好比添加事件監聽器,或是其餘只須要執行一次的複雜操做
},
inserted: function (newValue, oldValue) {
// 被綁定元素插入父節點時調用
},
update: function (newValue, oldValue) {
// 根據得到的新值執行對應的更新
// 對於初始值也會被調用一次
},
unbind: function () {
// 作清理工做
// 好比移除在 bind() 中添加的事件監聽器
}
})

指令定義函數提供了幾個鉤子函數(可選):express

bind: 只調用一次,指令第一次綁定到元素時調用,用這個鉤子函數能夠定義一個在綁定時執行一次的初始化動做。
inserted: 被綁定元素插入父節點時調用(父節點存在便可調用,沒必要存在於 document 中)。
update: 所在組件的 VNode 更新時調用,可是可能發生在其孩子的 VNode 更新以前。指令的值可能發生了改變也可能沒有。可是你能夠經過比較更新先後的值來忽略沒必要要的模板更新 (詳細的鉤子函數參數見下)。
componentUpdated: 所在組件的 VNode 及其孩子的 VNode 所有更新時調用。
unbind: 只調用一次, 指令與元素解綁時調用。 
接下來咱們來看一下鉤子函數的參數 (包括 el,binding,vnode,oldVnode) 。
二、鉤子函數參數async

鉤子函數被賦予瞭如下參數:函數

el: 指令所綁定的元素,能夠用來直接操做 DOM 。
binding: 一個對象,包含如下屬性: 
name: 指令名,不包括 v- 前綴。
value: 指令的綁定值, 例如: v-my-directive=」1 + 1」, value 的值是 2。
oldValue: 指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子中可用。不管值是否改變均可用。
expression: 綁定值的字符串形式。 例如 v-my-directive=」1 + 1」 , expression 的值是 「1 + 1」。
arg: 傳給指令的參數。例如 v-my-directive:foo, arg 的值是 「foo」。 
modifiers: 一個包含修飾符的對象。 例如: v-my-directive.foo.bar, 修飾符對象 modifiers 的值是 { foo: true, bar: true }。
vnode: Vue 編譯生成的虛擬節點
oldVnode: 上一個虛擬節點,僅在 update 和 componentUpdated 鉤子中可用。
三、VNode接口this

export default class VNode {
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
functionalContext: Component | void; // only for functional component root nodes
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
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;.net

constructor (
tag?: string,
data?: VNodeData,
children?: ?Array<VNode>,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions,
asyncFactory?: Function
) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
this.ns = undefined
this.context = context
this.functionalContext = undefined
this.key = data && data.key
this.componentOptions = componentOptions
this.componentInstance = undefined
this.parent = undefined
this.raw = false
this.isStatic = false
this.isRootInsert = true
this.isComment = false
this.isCloned = false
this.isOnce = false
this.asyncFactory = asyncFactory
this.asyncMeta = undefined
this.isAsyncPlaceholder = false
}ssr

// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child (): Component | void {
return this.componentInstance
}
}component

export const createEmptyVNode = (text: string = '') => {
const node = new VNode()
node.text = text
node.isComment = true
return node
}server

export function createTextVNode (val: string | number) {
return new VNode(undefined, undefined, undefined, String(val))
}對象

// optimized shallow clone
// used for static nodes and slot nodes because they may be reused across
// multiple renders, cloning them avoids errors when DOM manipulations rely
// on their elm reference.
export function cloneVNode (vnode: VNode, deep?: boolean): VNode {
const cloned = new VNode(
vnode.tag,
vnode.data,
vnode.children,
vnode.text,
vnode.elm,
vnode.context,
vnode.componentOptions,
vnode.asyncFactory
)
cloned.ns = vnode.ns
cloned.isStatic = vnode.isStatic
cloned.key = vnode.key
cloned.isComment = vnode.isComment
cloned.isCloned = true
if (deep && vnode.children) {
cloned.children = cloneVNodes(vnode.children)
}
return cloned
}

export function cloneVNodes (vnodes: Array<VNode>, deep?: boolean): Array<VNode> {
const len = vnodes.length
const res = new Array(len)
for (let i = 0; i < len; i++) {
res[i] = cloneVNode(vnodes[i], deep)
}
return res
}

四、VNode

不存在父節點:

 

存在父節點:

 

v-IME自定義指令示例:

import { log } from 'SUtil'
const install = Vue => {
let isBind = true
let IMEService = ''
let $this = new Vue({
data () {
return {
outEl: null,
outVnode: null
}
},
watch: {
outVnode (value, oldValue) {
if (oldValue) {
oldValue.componentInstance.isFocus = false
}
}
}
})
async function openIme (e, option) {
log.d('openIme', isBind, $this.$data.outVnode.componentInstance.percentValue)
if ($this.$data.outEl && IMEService && isBind) {
log.d('open')
$this.$data.outEl.target.focus()
let ret = await IMEService.open({
type: IMEService[option.type],
track: Number.parseInt(option.track),
w: Number.parseInt(option.w),
h: Number.parseInt(option.h),
x: Number.parseInt(option.x),
y: Number.parseInt(option.y)
})
if (ret.isSuccess) {
console.log('輸入法開啓成功')
}
}
}
async function closeIme (e) {
log.d('closeIme', isBind, $this.$data.outVnode.componentInstance.percentValue)
if ($this.$data.outEl && IMEService && isBind) {
log.d('close')
$this.$data.outVnode.componentInstance.isFocus = false
let ret = await IMEService.close()
if (ret.isSuccess) {
console.log('輸入法關閉成功')
}
}
}
Vue.directive('IME', {
bind: function (el, binding, vnode) {
$this.$data.outVnode = vnode
if (window.taf && window.taf.service.IMEService) {
IMEService = window.taf.service.IMEService
} else {
log.e('IMEService 不存在')
$this.$data.outVnode.componentInstance.$emit('error', '輸入法故障')
log.d('error', $this.$data.outVnode.componentInstance.percentValue)
}
},
inserted: function (el, binding, vnode) {
const inputDom = vnode.componentInstance.$el.querySelector('input')
if (inputDom) {
inputDom.addEventListener('focus', async focusEvent => {
log.d('focus')
$this.$data.outEl = focusEvent
$this.$data.outVnode = vnode
vnode.componentInstance.ctrlBlur = true
openIme(focusEvent, binding.value)
})
inputDom.addEventListener('click', async focusEvent => {
log.d('click')
$this.$data.outEl = focusEvent
$this.$data.outVnode = vnode
vnode.componentInstance.ctrlBlur = true
openIme(focusEvent, binding.value)
})
inputDom.addEventListener('blur', async focusEvent => {
log.d('blur')
$this.$data.outEl = focusEvent
$this.$data.outVnode = vnode
vnode.componentInstance.ctrlBlur = false
closeIme(focusEvent)
})
}
isBind = true
},
async unbind (el, binding, vnode) {
log.d('unbind')
if (window.taf && window.taf.service.IMEService) {
IMEService = window.taf.service.IMEService
} else {
log.e('IMEService 不存在')
$this.$data.outVnode.componentInstance.$emit('error', 'IMEService 不存在')
log.d('error', $this.$data.outVnode.componentInstance.percentValue)
}
const inputDom = vnode.componentInstance.$el.querySelector('input')
if (inputDom) {
inputDom.removeEventListener('click', openIme)
isBind = false
await IMEService.close()
}
}
})
}
export default {
install
}

--------------------- 原文:https://blog.csdn.net/qq_27626333/article/details/77743719

相關文章
相關標籤/搜索