vue自定义指令原理

vue自定义指令原理vue 指令本质指令本质上是装饰器 是 vue 对 HTML 元素的扩展 给 HTML 元素增加自定义功能 语义化 HTML 标签 vue 编译 DOM 时 会执行与指令关联的 JS 代码 即找到指令对象 执行指令对象的相关方法 自定义指令生命周期自定义指令有五个生命周期 也叫钩子函数 分别是 bind inserted update componentUpd unbind 钩子函数作用介绍 bind 只调用

vue指令本质

指令本质上是装饰器,是vue对HTML元素的扩展,给HTML元素增加自定义功能,语义化HTML标签。vue编译DOM时,会执行与指令关联的JS代码,即找到指令对象,执行指令对象的相关方法。

自定义指令生命周期

自定义指令有五个生命周期(也叫钩子函数),分别是bind、inserted、update、componentUpdated、unbind

钩子函数作用介绍

  1. bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个绑定时执行一次的初始化动作。
  2. inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
  3. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。
  4. componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
  5. unbind:只调用一次,指令与元素解绑时调用。

实现过程

源码

// 版本2.6.10 export default { 
    create: updateDirectives, update: updateDirectives, destroy: function unbindDirectives (vnode: VNodeWithData) { 
    updateDirectives(vnode, emptyNode) } } function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) { 
    if (oldVnode.data.directives || vnode.data.directives) { 
    _update(oldVnode, vnode) } } function _update (oldVnode, vnode) { 
    const isCreate = oldVnode === emptyNode // 判断虚拟节点是否是一个新创建的节点 const isDestroy = vnode === emptyNode // 当新的虚拟节点不存在,在旧虚拟节点存在时,为true const oldDirs = normalizeDirectives(oldVnode.data.directives, oldVnode.context) // 旧指令集合 const newDirs = normalizeDirectives(vnode.data.directives, vnode.context) // 新指令集合 const dirsWithInsert = [] // 保存需要触发inserted指令钩子的列表 const dirsWithPostpatch = [] // 保存需要触发componentUpdated指令钩子的列表 let key, oldDir, dir for (key in newDirs) { 
    oldDir = oldDirs[key] dir = newDirs[key] if (!oldDir) { 
    // 判断oldDir是否存在,如果不存在,则首次绑定到元素中 // 调用bind callHook(dir, 'bind', vnode, oldVnode) // 判断指令是否有inserted方法,有则添加到dirsWithInsert,保证执行完指令的bind方法后执行inserted方法 if (dir.def && dir.def.inserted) { 
    dirsWithInsert.push(dir) } } else { 
    // oldDir存在,则更新指令 dir.oldValue = oldDir.value dir.oldArg = oldDir.arg callHook(dir, 'update', vnode, oldVnode) // 判断指令是否有componentUpdated方法,有则添加到dirsWithPostpatch, // 保证指令所在的vnode及自vnode更新完后(执行完指令的update方法后),执行componentUpdated方法 if (dir.def && dir.def.componentUpdated) { 
    dirsWithPostpatch.push(dir) } } } if (dirsWithInsert.length) { 
    const callInsert = () => { 
    for (let i = 0; i < dirsWithInsert.length; i++) { 
    callHook(dirsWithInsert[i], 'inserted', vnode, oldVnode) } } if (isCreate) { 
    // 如果是新创建的节点,使用mergeVNodeHook将一个钩子函数与虚拟节点现有的钩子函数合并在一起 // 可以将钩子函数的执行推迟到被绑定的元素插入到父节点之后进行 mergeVNodeHook(vnode, 'insert', callInsert) } else { 
    callInsert() } } if (dirsWithPostpatch.length) { 
    mergeVNodeHook(vnode, 'postpatch', () => { 
    for (let i = 0; i < dirsWithPostpatch.length; i++) { 
    callHook(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode) } }) } // 先判断当前虚拟节点是否是新创建 if (!isCreate) { 
    // 循环旧指令集合,找出不存在的,则该指令是废弃的,并执行指令的unbind方法 for (key in oldDirs) { 
    if (!newDirs[key]) { 
    callHook(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy) } } } } const emptyModifiers = Object.create(null) function normalizeDirectives ( dirs: ?Array<VNodeDirective>, vm: Component ): { 
    [key: string]: VNodeDirective } { 
    const res = Object.create(null) if (!dirs) { 
    return res } let i, dir for (i = 0; i < dirs.length; i++) { 
    dir = dirs[i] if (!dir.modifiers) { 
    dir.modifiers = emptyModifiers } res[getRawDirName(dir)] = dir dir.def = resolveAsset(vm.$options, 'directives', dir.name, true) } return res } function getRawDirName (dir: VNodeDirective): string { 
    return dir.rawName || `${ 
     dir.name}.${ 
     Object.keys(dir.modifiers || { 
     }).join('.')}` } function callHook (dir, hook, vnode, oldVnode, isDestroy) { 
    const fn = dir.def && dir.def[hook] if (fn) { 
    try { 
    fn(vnode.elm, dir, vnode, oldVnode, isDestroy) } catch (e) { 
    handleError(e, vnode.context, `directive ${ 
     dir.name} ${ 
     hook} hook`) } } } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224091.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午12:43
下一篇 2026年3月17日 下午12:43


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号