自定义指令

目录

一.什么是自定义指令

二.定义指令

1.全局自定义指令

2.组件局部自定义指令

3.指令使用 

4.绑定值的自定义指令

三.自定义指令钩子函数和参数

1.钩子函数

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

(2)bind:只调用一次,指令第一次绑定到元素上时调用,可以进行一些初始化的操作。

注意:bind 钩子函数在 inserted 钩子函数之前调用。

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

(4)componentUpdated:指令所在组件的 VNode 和子 VNode 全部更新后调用。

(5)unbind:只调用一次,在指令和元素解绑时调用。

2.钩子函数参数

el:绑定的元素,可用来进行一些 DOM 操作。

binding:一个对象:包含以下属性:

vnode:Vue 编译生成的虚拟节点。

oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 函数中使用。

注意:除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

3.动态指令参数

4.函数简写 

四.关于在 elementui 上使用自定义指令

五.vue3的自定义指令

1.钩子函数

2.参数

3.函数简写

六.vue3封装指令库

1.创建一个盒子位置固定的指令:src/directives/position.js

2.创建自定义指令统一管理文件:src/directives/index.js

3.批量注册自定义指令:src/main.js

4.调用全局自定义指令:src/views/Directive.vue


一.什么是自定义指令

Vue除了核心功能内置的指令如 v-model,v-if 之外,还允许我们自己定义指令。通过这些指令可以对 dom 元素进行底层操作。

二.定义指令

1.全局自定义指令

通过 directive 方法去定义全局指令。

inserted 函数是指令对象可以定义的几个钩子函数之一,表示被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

钩子函数有很多参数,el 是其中之一,表示指令绑定的元素,可以用来进行一些 DOM 操作。

// main.js

// 注册一个全局自定义指令 `v-focus`
Vue.directive("focus", {
  // 当被绑定的元素插入到 DOM 中时触发
  inserted(el) {
    // 聚焦元素
    el.focus();
  },
});

2.组件局部自定义指令

export default {
  directives: {
    focus: {
      // 指令的定义
      inserted(el) {
        el.focus();
      },
    },
  },
};

3.指令使用 

在需要操作的 dom 元素上绑定自己定义的指令。

  <div class="directive">
    <input style="width: 200px" v-focus />
  </div>

此时页面刚渲染以后,input 标签就会获取焦点。

如果两个 input 都绑定 v-focus 指令,那么第二个 input 会获取标签。

<template>
  <div class="directive">
    <input style="width: 200px" v-focus /><br>
    <input style="width: 200px" v-focus />
  </div>
</template>

4.绑定值的自定义指令

钩子函数还有另一个参数 binding,是一个对象,其中的 value 属性就是指令的绑定值。

<template>
  <!-- 绑定值的自定义指令 -->
  <div class="directive">
    <div v-test="name"></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: "我是名字",
    };
  },
  directives: {
    test: {
      inserted(el, binding) {
        el.innerHTML = binding.value;
      },
    },
  },
};
</script>

三.自定义指令钩子函数和参数

前文提到了 inserted 函数和 el、binding参数,都只是自定义指令对象中的一部分。

1.钩子函数

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

(2)bind:只调用一次,指令第一次绑定到元素上时调用,可以进行一些初始化的操作。

注意:bind 钩子函数在 inserted 钩子函数之前调用。

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

(4)componentUpdated:指令所在组件的 VNode 和子 VNode 全部更新后调用。

(5)unbind:只调用一次,在指令和元素解绑时调用。

2.钩子函数参数

el:绑定的元素,可用来进行一些 DOM 操作。

binding:一个对象:包含以下属性:

  • name:指令名:不包含 v- 前缀。
  • value:指令绑定的值,例如:v-test="1 + 1" 中,绑定值为 2。
  • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 函数中使用,无论值是否改变都可用。
  • expression:字符串形式的指令表达式,例如 v-test="1 + 1" 中,表达式为 "1 + 1"。
  • arg:传给指令的参数,例如 v-test:foo 中参数为 foo。
  • modifiers:一个包含修饰符的对象,例如 v-test.foo.bar 中,修饰符对象为 {foo:true,bar:true} 。

vnode:Vue 编译生成的虚拟节点。

oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 函数中使用。

注意:除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

3.动态指令参数

指令的参数可以动态,例如 v-test:[argument] = "value" 中,argument 参数可以根据组件实例数据更新。

比如创建一个自定义指令,用于固定布局将元素固定在页面上,可以创建一个通过指令来更新元素位置的自定义指令:

<template>
  <div id="baseexample">
    <p>Scroll down the page</p>
    <p v-pin="200">Stick me 200px from the top of the page</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
    };
  },
  directives: {
    pin: {
      bind(el, binding, vnode) {
        el.style.position = "fixed";
        el.style.top = binding.value + "px";
      },
    },
  },
};
</script>

这会把该元素固定在距离页面顶部 200 像素的位置。但如果场景是我们需要把元素固定在左侧而不是顶部又该怎么办呢?这时使用动态参数就可以非常方便地根据每个组件实例来进行更新。

<template>
  <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>
</template>

<script>
export default {
  data() {
    return {
      direction: "left",
    };
  },
  directives: {
    pin: {
      bind(el, binding, vnode) {
        el.style.position = "fixed";
        var s = binding.arg == "left" ? "left" : "top";
        el.style[s] = binding.value + "px";
      },
    },
  },
};
</script>

4.函数简写 

当只有 bind 和 update 函数时,可以简写。

Vue.directive('color-swatch', function (el, binding) {
  el.style.backgroundColor = binding.value
})

四.关于在 elementui 上使用自定义指令

比如在给 el-input 组件上添加自定义指令使其自动获取焦点。

<template>
  <div class="directive">
    <el-input style="width: 200px" v-focus />
  </div>
</template>

<script>
export default {
  directives: {
    focus: {
      // 指令的定义
      inserted(el) {
        el.focus();
      },
    },
  },
};
</script>

 但是实际上是没有效果的。

那为什么绑定在 input 标签就生效,但是绑定在 el-input 标签不生效呢?这是因为 el-input 的结构问题。

el-input 的 DOM 结构是下面这样的。

<div class="el-input">
    <input type="text" autocomplete="off" placeholder="请输入内容" class="el-input__inner">
</div>

所以我们绑定 el-input 标签的时候,其实是绑定在 div 上的而不是 input 上的,这个时候 el.focus() 就没有用了。

所以为了让指令兼容 el-input,应该做一些修改。

  directives: {
    focus: {
      // 指令的定义
      inserted(el) {
        if (el.tagName.toLocaleUpperCase() !== "INPUT") {
          el = el.getElementsByTagName("input")[0];
        }
        el.focus();
      },
    },
  },

五.vue3的自定义指令

只是做了钩子函数和参数的部分修改 。

1.钩子函数

  directives: {
    focus: {
      // 在绑定元素的 attribute 前
      // 或事件监听器应用前调用
      created(el, binding, vnode) {
        // 下面会介绍各个参数的细节
      },
      // 在元素被插入到 DOM 前调用
      beforeMount(el, binding, vnode) {},
      // 在绑定元素的父组件
      // 及他自己的所有子节点都挂载完成后调用
      mounted(el, binding, vnode) {},
      // 绑定元素的父组件更新前调用
      beforeUpdate(el, binding, vnode, prevVnode) {},
      // 在绑定元素的父组件
      // 及他自己的所有子节点都更新后调用
      updated(el, binding, vnode, prevVnode) {},
      // 绑定元素的父组件卸载前调用
      beforeUnmount(el, binding, vnode) {},
      // 绑定元素的父组件卸载后调用
      unmounted(el, binding, vnode) {},
    },
  },

2.参数

el:指令绑定到的元素。这可以用于直接操作 DOM。

binding:一个对象,包含以下属性。

  • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2
  • oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
  • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
  • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
  • instance:使用该指令的组件实例。
  • dir:指令的定义对象。

vnode:代表绑定元素的底层 VNode。

prevVnode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。

3.函数简写

当只有mounted 和 updated 上实现相同的行为,除此之外并不需要其他钩子时可以简写。

app.directive('color', (el, binding) => {
  // 这会在 `mounted` 和 `updated` 时都调用
  el.style.color = binding.value
})

六.vue3封装指令库

1.创建一个盒子位置固定的指令:src/directives/position.js

const pos = {
  mounted(el, binding) {
    // 传递给指令的值:盒子是否要固定
    let pinned = binding.value
    // 传递给指令的修饰符:表示盒子定在哪里
    let position = binding.modifiers
    // 传递给指令的参数:可以表示盒子的特性
    let args = binding.arg
    
    if (pinned) {
      el.style.position = 'fixed'
      if (args == 'warning') {
        el.style.backgroundColor = 'pink'
      } else {
        el.style.backgroundColor = 'gray'
      }
      for (let val in position) {
        if (position[val]) {
          el.style[val] = '10px'
        }
      }
    } else {
      el.style.position = 'static'
      el.style.backgroundColor = ''
    }
  },
}
export default pos

2.创建自定义指令统一管理文件:src/directives/index.js

import pos from './position'
const directives = {
  pos,
}
export default {
  install(app) {
    Object.keys(directives).forEach((key) => {
      app.directive(key, directives[key])
    })
  },
}

3.批量注册自定义指令:src/main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import Directives from './directives/index.js'
const app = createApp(App)

app.use(Directives)
app.use(router).mount('#app')

4.调用全局自定义指令:src/views/Directive.vue

<template>
  <div class="directive">
    <div class="box" v-pos:warning.right.top="true">{{msg}}</div>
    <div class="box" v-pos:normal.right.bottom="true">{{msg}}</div>
    <div class="box" v-pos:normal.left.bottom="true">{{msg}}</div>
    <div class="box">{{msg}}</div>
  </div>
</template>

<script>
import { defineComponent, ref } from 'vue'

export default defineComponent({
  name: 'Directive',
  setup() {
    const msg = ref('message')
    return {
      msg
    }
  },
})
</script>

<style scoped>
.box {
  height: 100px;
  width: 100px;
  text-align: center;
  line-height: 100px;
  color: aliceblue;
  background-color: green;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值