Vue自定义指令

Vue指令:

在使用Vue框架进行前端开发时,我们经常会使用一些特殊指令来快速实现一些效果或功能。
常见指令如:v-bind、v-if (v-else)、v-show、v-html等等都是一些比较常用的指令
使用如下:

<span v-if=true v-html="htmlContext || '--'"></span>
<span v-show=true> show html </span>

由于本文主要介绍自定义指令相关的一些知识,所以对于Vue自带指令就不做过多赘述了
在这情况下,Vue官方也推出了一种编写自定义指令的方法。我们可以定义开发我们自定义的模版指令,来对一些特殊的需求效果功能进行开发与实现
在Vue的官方文档中是如下描述的:

vue自定义指令官方文档

自定义指令不只支持全局注册,也支持在组件中进行注册并引用,具体操作在文档中都有详细描述

Vue自定义指令

Vue自定义指令机制包含五个钩子函数,每个钩子函数包含四个回调参数
钩子函数简单来说可以称作为生命周期,如果对生命周期有过了解或应用应该会比较容易理解

Vue2钩子函数:

bind:只调用一次,组件初始化时会调用该钩子函数
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。可以通过比较更新前后的值来忽略不必要的模板更新
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用
unbind:只调用一次,指令与元素解绑时调用

Vue3钩子函数:

created - 新增!在元素的 attribute 或事件监听器被应用之前调用。
bind → beforeMount
inserted → mounted
beforeUpdate:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。
update → 移除!该钩子与 updated 有太多相似之处,因此它是多余的。请改用 updated
componentUpdated → updated
beforeUnmount:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用
unbind -> unmounted

四个函数的回调参数:el、binding、vnode、oldVnode
el:指令所绑定的元素
binding(对象):
name(指令名称)、value(指令的绑定值)、oldValue(指令绑定的前一个值)、
expression(绑定指令的表达式)、arg(绑定的指令参数)、
modifiers(一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true })
vnode:即Vue虚拟DOM生成的虚拟节点,细节可学习Vue虚拟DOM原理
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子函数中可用

接下来上一个简单的组件注册自定义指令的例子:
效果为一渲染input元素后即添加焦点,可辅助进行表单校验

// html
<input v-focus> //  JSdirectives: {focus: {// 指令的定义inserted: function (el,binding,vnode,oldVnode) {el.focus();// 展示回调值输出console.log('el',el);console.log('binding',binding);console.log('vnode',vnode);console.log('oldVnode',oldVnode);}}}

输出如下:

Vue自定义指令原理:

以上介绍了Vue自定义指令的相关介绍以及用法
接下来将集中分享一下关于Vue自定义指令的实现原理
首先我们知道,
Vue一个机制首先要在 vm.$options 上进行挂载 => 而后进行正则解析处理 => 最后生成虚拟dom

这个的流程就不在此赘述了,有时间会有集中出一篇关于这方面的文章
接下来我们直接分析patchDom的过程
源码链接:Vue自定义指令源码 Github
为了页面不占用太大篇幅,源码就不全额摘抄了,下面将集中分析部分关键原理

/* @flow */import { emptyNode } from 'core/vdom/patch'
import { resolveAsset, handleError } from 'core/util/index'
import { mergeVNodeHook } from 'core/vdom/helpers/index'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 === emptyNodeconst isDestroy = vnode === emptyNode// 新旧节点的指令抽离const oldDirs = normalizeDirectives(oldVnode.data.directives, oldVnode.context)const newDirs = normalizeDirectives(vnode.data.directives, vnode.context)// inserted 需要触发钩子函数的元素列表const dirsWithInsert = []// componentUpdatedconst dirsWithPostpatch = []// 更新新旧节点 指令let key, oldDir, dirfor (key in newDirs) {oldDir = oldDirs[key]dir = newDirs[key]// 不存在旧节点时,直接插入!if (!oldDir) {// new directive, bindcallHook(dir, 'bind', vnode, oldVnode)if (dir.def && dir.def.inserted) {dirsWithInsert.push(dir)}// 存在旧节点时:更新      } else {// existing directive, updatedir.oldValue = oldDir.valuedir.oldArg = oldDir.argcallHook(dir, 'update', vnode, oldVnode)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(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) {// 遍历旧节点for (key in oldDirs) {// 寻找不存在的节点if (!newDirs[key]) {// 执行指令的unbind方法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) {// $flow-disable-linereturn res}let i, dirfor (i = 0; i < dirs.length; i++) {dir = dirs[i]if (!dir.modifiers) {// $flow-disable-linedir.modifiers = emptyModifiers}res[getRawDirName(dir)] = dirdir.def = resolveAsset(vm.$options, 'directives', dir.name, true)}// $flow-disable-linereturn res
}// 获取指令name
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`)}}
}

Vue自定义指令介绍及原理相关推荐

  1. Vue自定义指令介绍

    [自定义指令灵魂三问] 是什么:是程序员在vue中自己定义的指令,用在标签上 为什么:为了获取标签,扩展额外的功能(vue自带的指令不能满足我们的需求时用) 怎么用:全局使用 和 局部使用 1.全局自 ...

  2. Vue自定义指令及实现图片懒加载指令

    一. 速识概念:   在我们使用 Vue 的过程中,遇到了很多方便我们操作的vue内置指令,以 v-xxx 表示.比如有 v-module,v-for,v-if,v-show 等等,每个指令都能实现一 ...

  3. clientsideevents能定义几个click事件_分享8个非常实用的Vue自定义指令

    作者:lzg9527 https://juejin.cn/post/6906028995133833230 在 Vue,除了核心功能默认内置的指令 ( v-model 和 v-show ),Vue 也 ...

  4. vue自定义指令实现按钮界别权限管理

    在后台管理系统中,每个用户权限都往往需要严格分配,某些可能只具有某个列表的查看功能而没有编辑功能,这就需要对后台管理系统的每个按钮进行权限管理 自定义指令管理权限原理: 1.自定义指令,根据标签参数实 ...

  5. 超实用:Vue 自定义指令合集

    大家好,我是漫步 前面我已经分享过一篇"分享8个非常实用的Vue自定义指令"文章,里面教了大家如何使用自定义指令,以及几个使用的指令,下面这篇文章也是类似内容,希望你会喜欢.记得查 ...

  6. vue自定义指令封装节流_Vue自定义指令封装节流函数的方法示例

    节流函数是web前端开发中经常用到的一个开发技巧,在input实时搜索,滚动事件等,为了避免过多消耗性能,我们都会使用节流函数.在<JavaScript高级程序设计>一书中有这样的一个例子 ...

  7. vuejs 指令封装 button 加载效果_这些Vue自定义指令,让你的项目开发爽到爆

    受 AngularJS 的启发,Vue 内置了一些非常有用的指令(比如v-html 和 v-once等),每个指令都有自身的用途.完整的指令列表可以在这里查看. 这还没完,更棒的是可以开发自定义指令. ...

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

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

  9. pyqt5 treeview鼠标右键菜单事件_【动手实践】使用 Vue 自定义指令实现右键菜单...

    本文来自于 神奇的程序员 前言 浏览器里右键时会有一个默认的菜单,在我的开源项目中正好有自定义右键菜单的需求,在npm库找了下与之相关的包,发现都是以组件形式实现的,感觉那种做法太过繁琐. 于是,我就 ...

最新文章

  1. whlie and for
  2. Tensorflow官方文档中文版——第一章
  3. c 语言epc编码如何解开,EPC编码结构
  4. gcc/g++ 编译器出现 undefined reference to ‘这里是函数名‘,往往意味这这个函数没有定义
  5. .NET团队送给.NET开发人员的云原生学习资源
  6. Socket代码实现服务端 和 客户端之间通信
  7. Spring全家桶——SpringCloud之Feign(Finchley版)
  8. D3DXMatrixMultiply 函数
  9. 程序语言python如何抓取信息_python中系统信息获取psutil使用详解
  10. TeXmacs - 所见即所得 - 专业排版软件
  11. 代码整洁之道 垃圾编码收集
  12. 怎么把做好的ps保存成图片_ps存成jpg格式的快捷键,ps如何另存为图片格式
  13. 无线网卡 VS 无线上网卡
  14. RDA5820收音机芯片驱动
  15. Norms in Matrix and Vector
  16. 最大流 紧急疏散evacuate
  17. 更新显卡驱动后黑屏无法进入系统的解决办法
  18. 测试人员面试需要掌握的内容
  19. 2020.9.28(Hive视图、索引、权限管理)
  20. springboot gradle 打包排除依赖 排除文件

热门文章

  1. Python:披萨店主页程序练习
  2. 飞宇计算机专业,廉飞宇
  3. git提交错误 Remote reject HEAD - refs/for/master (change htto://gerrit.iot.cn/54 closed)
  4. 转 【调侃】IOC前世今生
  5. opencv棋盘格角点检测原理总结
  6. 处理票据识别的预处理
  7. 文字编排的对齐方式——错位
  8. Web API 项目报“Unable to resolve service for type ”
  9. 专利申请原则 专利申请后有什么好处 怎么保护专利的权益?
  10. python加锁的方法_python 方法锁