1、自定义指令钩子函数

Vue.directive('my-directive', {
bind: function () {
// 做绑定的准备工作
// 比如添加事件监听器,或是其他只需要执行一次的复杂操作
},
inserted: function (newValue, oldValue) {
// 被绑定元素插入父节点时调用
},
update: function (newValue, oldValue) {
// 根据获得的新值执行对应的更新
// 对于初始值也会被调用一次
},
unbind: function () {
// 做清理工作
// 比如移除在 bind() 中添加的事件监听器
}
})

指令定义函数提供了几个钩子函数(可选):

bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
update: 所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。指令的值可能发生了改变也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated: 所在组件的 VNode 及其孩子的 VNode 全部更新时调用。
unbind: 只调用一次, 指令与元素解绑时调用。 
接下来我们来看一下钩子函数的参数 (包括 el,binding,vnode,oldVnode) 。
2、钩子函数参数

钩子函数被赋予了以下参数:

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 钩子中可用。
3、VNode接口

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;

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
}

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

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

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
}

4、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

vue自定义指令VNode详解(转)的更多相关文章

  1. angular 自定义指令参数详解【转】【个人收藏用】

    restrict:指令在dom中的声明形式 E(元素)A(属性)C(类名)M(注释) priority优先级:一个元素上存在两个指令,来决定那个指令被优先执行 terminal:true或false, ...

  2. angular 自定义指令参数详解

    restrict:指令在dom中的声明形式 E(元素)A(属性)C(类名)M(注释) priority优先级:一个元素上存在两个指令,来决定那个指令被优先执行 terminal:true或false, ...

  3. Vue自定义指令使用方法详解 和 使用场景

    Vue自定义指令的使用,具体内容如下 1.自定义指令的语法 Vue自定义指令语法如下: Vue.directive(id, definition) 传入的两个参数,id是指指令ID,definitio ...

  4. vue自定义指令

    Vue自定义指令: Vue.directive('myDr', function (el, binding) { el.onclick =function(){ binding.value(); } ...

  5. 每个人都能实现的vue自定义指令

    前文 先来bb一堆废话哈哈.. 用vue做项目也有一年多了.除了用别人的插件之外.自己也没尝试去封装指令插件之类的东西来用. 刚好最近在项目中遇到一个问题.(快速点击按钮多次触发多次绑定的方法),于是 ...

  6. vue 自定义指令(directive)实例

    一.内置指令 1.v-bind:响应并更新DOM特性:例如:v-bind:href  v-bind:class  v-bind:title  v-bind:bb 2.v-on:用于监听DOM事件: 例 ...

  7. Vue自定义指令使用场景

    当你第一次接触vue的时候,一定会使用到其中的几个指令,比如:v-if.v-for.v-bind...这些都是vue为我们写好的,用起来相当的爽.如果有些场景不满足,需要我们自己去自定义,那要怎么办呢 ...

  8. 使用Vue自定义指令实现Select组件

    完成的效果图如下: 一.首先,我们简单布局一下: <template> <div class="select"> <div class="i ...

  9. vue 自定义指令的魅力

    [第1103期]vue 自定义指令的魅力 点点 前端早读课 2017-11-08 前言 很多事情不能做过多的计划,因为计划赶不上变化.今日早读文章由富途@点点翻译分享. 正文从这开始- 在你初次接触一 ...

随机推荐

  1. linux 以导入文件形式添加定时任务(crontab)时需要注意的坑

    在实际操作过程中发现,使用导入文件形式添加定时任务时,会将用户已有的定时任务全部覆盖清理(先清空,再重新导入),所以在使用文件导入定时任务时,需要先将已有定时任务导出,然后将新任务进行追加到已有定时任 ...

  2. 配置本地 yum 仓库

    配置本地 yum 仓库 # yum 官网 http://yum.baseurl.org/ # yum 手册页 man yum man yum.conf SEE ALSO pkcon (1) yum.c ...

  3. Python基础之内置方法

    目录 字符串的内置方法 按索引取值 切片(顾头不顾尾,步长) 长度len 成员运算 移除两边空白strip 切分split 循环 lower&upper startswith & en ...

  4. VIJOS-P1146 宿舍里的故事之五子棋

    洛谷 P1479 宿舍里的故事之五子棋 洛谷传送门 JDOJ 1311: VIJOS-P1146 宿舍里的故事之五子棋 JDOJ传送门 题目描述 宿舍里好多好多有趣的事! 7890653今天看到不知何 ...

  5. 【java异常】Parsing error was found in mapping #{}. Check syntax #{property|(expression), var1=value1, var2=val

    在增删改查的过程中有一个#{ }没有填写

  6. JavaScript计算时间前一天跟后一天

    1.获取当前时 //写在HTML <button onclick="goBefore()">前一天</button> <button onclick= ...

  7. 30-ESP8266 SDK开发基础入门篇--SPI

    这节只是做记录, 整个的教程呢,重新整理下 教程有点乱,需要再细分一下 这节只是做一下我使用其SPI的记录 还是老样子,看人家LUA源码里面怎么使用的 注意哈,对于8266 SDK的学习我还是建议大家 ...

  8. 深入js系列-类型(开篇)

    思考 作为一个编程人员,你可能从来没仔细思考过,为什么这么多高级语言会有类型这东西. 实际上,类型有点类似生活中的类别,我们日常生活,早已经把这个概念理解到了,切肉和切水果会用不同的刀. 语言级别的类 ...

  9. .NET三层架构开发初步

    转自:https://www.cnblogs.com/weilengdeyu/archive/2013/01/04/2844847.html 今天咱们来谈下三层架构.说到三层架构,搞过点程序的可能都知 ...

  10. Win10,Anaconda,tensorflow-gpu安装教程

    ,参考于:https://www.cnblogs.com/guoyaohua/p/9265268.html 目录 前言 第一步:安装Anaconda 1.下载和安装 2.配置Anaconda环境变量 ...