VUE ---- 自定义指令的理解和应用

VUE ---- 自定义指令的理解和应用

前段时间在项目当中有一个需求要操作到DOM层面。尽管Vue框架推崇数据驱动视图的理念,但并非所有情况都适合数据驱动。考虑到上面提到的业务场景,Vue官方提供了自定义指令。因为指令的颗粒度比较细能够直击要害(DOM),而且具备有高度的灵活性,运用起来非常的方便。

一、概念

Vue中内置了很多的指令,如v-model、v-show、v-html等,但是有时候这些指令并不能满足我们,或者说我们想为底层元素(DOM)附加一些特别的功能,这时候,我们就需要用到vue中一个很强大的功能了—自定义指令(Vue.directive)。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。

这里敲黑板了!重要的事情上面已经说三遍了!自定义指令解决的问题或者说使用场景是对普通 DOM 元素进行底层操作,所以我们不能盲目的胡乱的使用自定义指令。

二、使用方式

tips:除了el对象之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行,以避免在开发过程中带来不必要的麻烦。

a. Vue2.0:

// 1.注册全局自定义指令
Vue.directive('xxx', {
    // 指令第一次绑定到元素时「初始化」……
    bind(el, binding, vnode, oldVnode) {
        // el:绑定指令的DOM元素对象
        // binding:数据对象
        //   + name:不带“v-”的指令名字
        //   + rawName:带“v-”的指令名字「含值和修饰符」
        //   + value:指令绑定的值   v-xxx="1+1" -> value:2
        //   + expression:指令表达式  v-xxx="1+1" -> expression:"1+1"
        //   + arg:传给指令的参数   v-xxx:n -> arg:"n"
        //   + modifiers:修饰符对象  v-xxx.stop.finish -> modifiers:{stop:true,finish:true}
        // vnode:Vue编译生成的虚拟节点
        // oldVnode:上一个虚拟节点
    },
    // 被绑定元素插入父节点时「父节点不一定非要插入到文档中,一般是插入到DOM中」……
    inserted() {},
    // 所在组件的VNode更新时「指令的值可能发生了改变,也可能没有」……
    update(el, binding, vnode, oldVnode) {
        // binding:数据对象
        //   + oldArg
        //   + oldValue
        //   + ...
    },
    // 所在组件的VNode及其子VNode全部更新后……
    componentUpdated() {},
    // 指令与元素解绑时……
    unbind() {}
});

// or

// 2.注册局部自定义指令(组件内)
directives:{
  xxx:{
	bind(el, binding, vnode, oldVnode) {},
	inserted() {},
	update(el, binding, vnode, oldVnode) {},
	unbind() {}
  }
}

i:列举官方实例

<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>

Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name: '       + s(binding.name) + '<br>' +
      'rawName:'    + s(binding.rawName) + '<br>' +
      'value: '      + s(binding.value) + '<br>' +
      'expression: ' + s(binding.expression) + '<br>' +
      'argument: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ')
  }
})

new Vue({
  el: '#hook-arguments-example',
  data: {
    message: 'hello!'
  }
})

// name: "demo" 不带“v-”的指令名字
// rawName: "v-demo" 带“v-”的指令名字「含值和修饰符」
// value: "hello!" 指令绑定的值   v-demo="message" -> value:hello!
// expression: "message" 指令表达式  v-demo="message" -> expression:"message"
// argument: "foo" 传给指令的参数   v-demo:foo -> arg:"foo"
// modifiers: {"a":true, "b":true} 修饰符对象  v-demo.a.b -> modifiers:{a:true,b:true}
// vnode keys: Vue编译生成的虚拟节点 -> tag,data,children,text,elm,ns,context,fnContext,fnScopeId,key,componentOptions,componentInstance,parent,raw,isStatic,isRootInsert,isComment,isCloned,isOnce,asyncFactory,asyncMeta,isAsyncPlaceholder

ii:动态指令参数

<div id="dynamicexample">
  <h3>Scroll down inside this section ↓</h3>
  <p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p>
</div>

Vue.directive('pin', {
  bind: function (el, binding, vnode) {
    el.style.position = 'fixed'
    var s = (binding.arg == 'left' ? 'left' : 'top') // s => left
    el.style[s] = binding.value + 'px'
  }
})

new Vue({
  el: '#dynamicexample',
  data: function () {
    return {
      direction: 'left'
    }
  }
})

iii:函数简写方式

在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子的时候,就可以采取函数简写的方式:

<div v-demo="{ color: 'white', text: 'hello!' }"></div>

Vue.directive('demo', function (el, binding) {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text)  // => "hello!"
})

iiii:对象字面量

如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。

<div v-demo="{ color: 'white', text: 'hello!' }"></div>

Vue.directive('demo', function (el, binding) {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text)  // => "hello!"
})

b. Vue3.0,和Vue2.0有了一些钩子的区别:

import {
    createApp
} from 'vue';
const app = createApp(App);

// 1.注册全局自定义指令
app.directive('xxx', {
	// 绑定元素之前
	created() {},
    // 指令首次绑定到元素且在安装父组件之前...「等同于bind」
    beforeMount(el, binding, vnode, prevVnode) {
        // binding:数据对象
        //   + instance: 使用指令的组件实例
        //   + arg:传给指令的参数   v-xxx:n -> arg:"n"
        //   + modifiers:修饰符对象 v-xxx.stop -> modifiers:{stop:true}
        //   + value:指令绑定的值   v-xxx="1+1" -> value:2
        //   + oldValue:之前绑定的值
        //   + dir: 一个对象,在注册指令时作为参数传递
    },
    // 安装绑定元素的父组件时...「等同于inserted」
    mounted() {},
    // 在包含组件的VNode更新之前...
    beforeUpdate() {},
    // 在包含组件的VNode及其子VNode更新后...「等同于componentUpdated」
    updated() {},
    // 在卸载绑定元素的父组件之前...
    beforeUnmount() {},
    // 指令与元素解除绑定且父组件已卸载时...「等同于unbind」
    unmounted() {}
});
app.mount('#app');

// or

// 2.注册局部自定义指令(组件内)

directives:{
  xxx:{
	beforeMount(el, binding, vnode, prevVnode) {},
	mounted() {},
	beforeUpdate() {},
	update() {},
	beforeUnmount() {},
	unmounted() {}
  }
}

i:官方提供实例

<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>

const app = Vue.createApp({
  data() {
    return {
      message: 'hello!'
    }
  }
})

app.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'value: '      + s(binding.value) + '<br>' +
      'arg: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ')
  }
})

app.mount('#hook-arguments-example')

// value: "hello!" 指令绑定的值   v-demo="message" -> value:hello!
// arg: "foo" 传给指令的参数   v-demo:foo -> arg:"foo"
// modifiers: {"a":true, "b":true} 修饰符对象  v-demo.a.b -> modifiers:{a:true,b:true}
// vnode keys: Vue编译生成的虚拟节点 -> tag,data,children,text,elm,ns,context,fnContext,fnScopeId,key,componentOptions,componentInstance,parent,raw,isStatic,isRootInsert,isComment,isCloned,isOnce,asyncFactory,asyncMeta,isAsyncPlaceholder

ii:动态指令参数

基本上和Vue2.0版本的例子一样,各位看官自行参考。

iii:函数简写方式

基本上和Vue2.0版本的例子一样,各位看官自行参考。

const app = Vue.createApp()

app.directive('pin', (el, binding) => {
  el.style.position = 'fixed'
  const s = binding.arg || 'top'
  el.style[s] = binding.value + 'px'
})

iiii:对象字面量

这种情况用来应付接收多个指令值,记住,指令函数能够接受所有合法的 JavaScript 表达式。

<div v-demo="{ color: 'white', text: 'hello!' }"></div>

app.directive('demo', (el, binding) => {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text) // => "hello!"
})

三、弊端

vue的自定义指令是一个比较容易引起内存泄漏的地方,原因就在于指令通常给元素绑定了事件,但是如果忘记了解绑,就会产生内存泄漏的问题。

所以在unbind也好在unmounted也好,都要针对绑定的事件进行消除,否则就会产生内存泄漏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值