2025年自定义指令解析

自定义指令解析自定义指令定义 v show Vue 的内置指令 通过这个指令能展示和隐藏节点 Vue 底层控制了该节点的 display 属性 自定义指令 在构建项目过程中 虽然我们都是使用的组件形式 但是在某些情况下 我们仍然需要对普通 DOM 元素进行底层操作

大家好,我是讯享网,很高兴认识大家。

自定义指令定义

v-show :Vue 的内置指令,通过这个指令能展示和隐藏节点 (Vue底层控制了该节点的 display 属性)

自定义指令:在构建项目过程中,虽然我们都是使用的组件形式,但是在某些情况下,我们仍然需要对普通DOM元素进行底层操作,这个时候就会用到自定义指令。

自定义指令既可以像 v-show 一样,不搭配属性值,也可以像 v-text配置属性值来实现特殊效果

自定义指令分类

自定义指令分为组件内部的自定义指令 和 全局自定义指令

组件内部的自定义指令:实现将现有 数值放大 10 倍

<div id='root'> <div>当前count值是:{ 
  
    
  {count}}</div> <div>放大十倍后的count值是 <span v-big='count'></span></div> <div><button @click='count++'>点击+1</button></div> </div> const vm = new Vue({ el: '#root', data() { return { count: 1 } }, }) 

讯享网

在这里插入图片描述
讯享网
页面报错,提示 big 指令解析失败。但是,这里的指令报错是 big,而不是写在节点上的 v-big 指令,这说明了 Vue 自动将我们的 自定义指令添加上了 v- 前缀。

我们在组件内部注册一个自定义指令,这样页面上就不会报错了,但是放大后的值还是无法展示

讯享网directives: { // 函数式自定义指令 big() { }, // 对象式自定义指令 big:{ } } 

组件内部自定义指令( directives )和 data 平级,类似于 过滤器 ,也是一个属性,内部可以注册多个自定义指令,内部的注册的自定义指令有两种写法,分别是函数式和对象式,函数式精简一点,对象式复杂一点,但是能处理一些细节上的问题。

自定义指令的使用

我们先注册函数式自定义指令来解决这个简单的需求

1、 v-text 的作用是拿到当前 count 且展示到 当前 span 标签内部,但是同样的方式对于 v-big 是不生效的,那 自定义 v-big 又是怎么工作的呢?说到这里可能会想到 计算属性的使用方式,通过返回一个值,然后替换掉当前的插值语法,来实现 处理后数据的展示,那我们也先这样来试试。

directives: { big() { return 900 } } 

在这里插入图片描述

通过页面展示发现,即使 给了返回值,还是没有展示出来,说明 v-big 并不是这样使用的。

通过查看 文档 之后发现,该自定义的指令钩子函数接收四个参数,分别是

el:指令所绑定的元素,是真实 DOM节点,可以通过该元素来操作 DOM
binding:一个对象,里面包括很多属性,但是基本上只关注 value 属性,因为这是 指令绑定的值
vnode:Vue 编译生成的虚拟节点
oldVnode:Vue 编译生成的上一个虚拟节点(仅在 update 和 componentUpdated 使用)
在这里插入图片描述
现在,我们能够得到这个 绑定的真实 DOM 节点,还能拿到当前节点绑定的 Value 值,那么我们就能以此来实现,针对改DOM元素的操作,例如完成上面的需求

讯享网directives: { big(el, binding, vnode, oldVnode) { console.log(el, binding, vnode, oldVnode) el.innerText = binding.value * 10 } } 

在这里插入图片描述
可以看到,通过直接操作 DOM 节点的 innertext ,放大10倍之后的值已经展示在页面上了。所以注册的自定义指令是并不需要返回值的,,而是通过直接操作 DOM 元素,来更改页面展示

调用自定义指令的时机

在上面的成功案例中,除了在第一次初始化时,自定义指令会调用一次,当点击按钮使得 count 增加时,发现自定义指令也是在被处重复调用的
在这里插入图片描述
这是不是就说明了,一旦自定义指令绑定的数据发生了变化,那么自定义指令就会被调用呢?就和计算属性一样,一旦以来的值发生了改变,就会重新调用计算属性方法。那我们验证一下,添加一个新的属性 name,值为 al,然后我们在控制台上修改 name 属性

..... <div>{ 
  
    
  {name}}</div> data() { return { name:'al', ..... } }, 

在这里插入图片描述
我们在控制台上修改了 name 属性,此时,与自定义指令 v-big 关联的 count 属性,并没有被修改,但是 自定义指令还是被触发了,这是因为 Vue 默认,一旦 data 中的响应式数据发生改变之后,就会重新解析模板内容,进而重新调用 自定义指令获取最新的值

2、当所在指令的组件或者叫模板被重新解析时( 包括但不限于 data 内部数据更改 )

自定义指令对象形式

需求升级,现在有一个input 框,绑定的还是 count 值,但是我需要在页面初始化的时候,input 框自动聚焦。可能有人会想到使用 autofocus 这个属性。

讯享网<input type="text" autofocus v-bind:value='count'> data() { return { count: 1 } }, 

1、自定义指令 focus,目的在于给 input 绑定 count 值,且使得 input 初始渲染之后直接聚焦

<input type="text" v-focus:value='count'> directives: { focus(el, binding) { el.value = binding.value el.focus() } } 

理论上来说,这样写是没有问题的,直接操作 DOM ,给 DOM 绑定 value 值,且利用本身的 focus 方法,使得 input 框自动聚焦。展示效果如下:
在这里插入图片描述但是实际上有问题,value 值是绑定了,但是自动聚焦却是没有做到,这又是为啥呢?
还有个问题,我添加一个按钮,点击之后 count 自增1,自定义指令代码不变

讯享网<input type="text" v-focus:value='count'> <div><button @click='count++'>点击+1</button></div> 

在这里插入图片描述
这个时候突然发现,这个input 框在我点击按钮 改变 count 之后,它自动聚焦了。这又是为什么呢?
其实应该这么说,这两个问题其实综合起来是一个问题,el.focus 这段代码其实是执行了的,但是执行的时机不对。我们用原生的js也可以实现这个效果

<button onclick="creatInput()">点我创建一个 input 元素</button> creatInput = () => { let i = document.createElement('input') document.body.appendChild(i) i.focus() } 

在这里插入图片描述
但是如果调换了 i.focus() 的位置,那就不会自动聚焦了

讯享网creatInput = () => { let i = document.createElement('input') i.focus() document.body.appendChild(i) } 

在这里插入图片描述

这是因为像这样的操作,需要在 input 这个元素添加到页面中之后,才会触发,否则页面都没有这个元素,设置 focus 没有意义。

所以,在自定义指令中,el.focus() 这句代码其实是执行了的,但是因为执行时机不对,所以页面初始化之后,不会自动聚焦。

那针对 Vue 需要怎么来理解呢?上面说过了,自定义指令的执行时机是 指令与元素成功绑定、当所在指令的组件或者叫模板被重新解析时,所以可以这么来理解

解决办法

其实解决办法就是,在 el 节点已经存在在页面上之后,再来执行 el.focus() ,但是要怎么获取这个时间点呢?我们到现在为止使用的还是 函数式的自定义指令 ,但是函数式自定义指令是无法获取DOM已经渲染到页面上这个准确时间点的,这也就是开篇说的,函数式无法处理一些细节问题。既然如此,那我们来试试对象形式的自定义指令

focus: { bind() { }, inserted() { }, update() { }, } 

对向形式的自定义指令储存在三个钩子函数,均为可选项

1、bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

2、inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已**入文档中)。

3、update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新

这三个钩子函数都能像 函数式自定义指令一样,接收到 el 和 binding ,VNode 和 oldVonde四个参数,但是后面两个参数使用频率较少,所以不详细介绍了。完整形式则是这样

讯享网focus: { bind(el, binding) { el.value = binding.value }, inserted(el, binding) { el.focus() }, update(el, binding) { console.log('update') }, } 

前面两个钩子函数这样写是没问题的,页面初始化之后展示的也是对的。
在这里插入图片描述
但是当点击按钮,更改 count 之后,模板重新编译,此时会跳过 bind 钩子 和 inserted 钩子,因为页面上已经存在这个节点了,,此时会直接触发 update 钩子,但是 update 钩子函数中没有做任何操作,所以 input 中展示的还是初始化之后的 count
在这里插入图片描述
要想更改 count 之后,input 框中的值也跟着变化,我们还需要在 update 钩子函数中做操作,重新将 input 中绑定的值替换为最新的 count 值。

update(el, binding) { el.value = binding.value }, 

这样看起来, bind 钩子 和 update 钩子 里面的代码是相同的,做的事情也是相同的,其实这就是 如果只看这两个钩子的话,这就是函数式自定义指令,只考虑初始化和更新后的操作,而不关心节点是否挂载

全局自定义指令:接收两个参数,第一个是字符串形式的指令名称,第二个是指令处理方式( 可以是函数式,也可以是对象式,具体就是把局部的处理函数或处理对象完整粘贴过来就行)

之前就有说过,自定义指令和自定义过滤器其实是极其类似的,组件内的 自定义过滤器只能在组件内部使用,同样的,组件内的自定义过滤器也只能在组件内部使用。那么全局的过滤器和全局的自定义指令同样也都是挂载到 全局 Vue 实例上的,且全局的过滤器和自定义指令都是一次只能注册一个,所以方法都是不带s复数形式的( filter ,directive )。例如

函数式的全局自定义指令:

讯享网Vue.directive('big', (el, binding) => { el.value = binding.value }) 

对象式的全局自定义指令:

Vue.directive('big', { bind(el, binding) { el.value = binding.value }, inserted(el, binding) { el.focus() }, update(el, binding) { el.value = binding.value } } ) 
小讯
上一篇 2025-03-14 16:16
下一篇 2025-01-06 08:51

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/26904.html