Skip to content
第 15 章 前端 ⏱ 12 分钟阅读

第 15 章:指令 ​

学习目标 ​

  • 自定义指令封装 DOM 行为
  • 理解钩子参数与生命周期
  • 写 v-permission / v-copy 等常用指令
  • 避坑:全局注册与命名

一、内置指令速查 ​

指令作用
v-text文本插值
v-html原始 HTML
v-show切换 display
v-if / v-else条件渲染
v-for列表
v-on / @事件
v-bind / :绑定
v-model双向
v-slot / #插槽
v-pre / v-cloak / v-once优化

二、自定义指令用法 ​

ts
// src/directives/permission.ts
import type { Directive } from 'vue'

export const permission: Directive<HTMLElement, string> = {
  mounted(el, binding) {
    const allow = checkPermission(binding.value)
    if (!allow) el.parentNode?.removeChild(el)
  },
}
vue
<button v-permission="'user:delete'">删除</button>

⚠️ 坑 1:v-permission 移除元素后,Vue 不知道节点被删了,反而会留下空引用,所以通常推荐禁用(disabled = true)。

三、指令生命周期 ​

ts
directive = {
  created(el, binding, vnode) {},       // 元素创建
  beforeMount(el, binding) {},
  mounted(el, binding) {},
  beforeUpdate(el, binding) {},
  updated(el, binding) {},
  beforeUnmount(el, binding) {},
  unmounted(el, binding) {},
}

参数说明:

  • el:DOM 元素
  • binding: { value, oldValue, arg, modifiers, dir, instance }

四、实战:v-copy ​

ts
export const copy: Directive<HTMLElement, string> = {
  mounted(el, binding) {
    el.addEventListener('click', async () => {
      await navigator.clipboard.writeText(binding.value)
      el.classList.add('copied')
      setTimeout(() => el.classList.remove('copied'), 1000)
    })
  },
}
vue
<button v-copy="text">复制</button>

五、实战:v-debounce ​

ts
export const debounce: Directive<HTMLElement, (...args: any[]) => void> = {
  mounted(el, binding) {
    let timer: number | undefined
    el.addEventListener('click', (e) => {
      if (timer) clearTimeout(timer)
      timer = window.setTimeout(() => binding.value(e), binding.arg ? +binding.arg : 300)
    })
  },
}
vue
<button v-debounce:500="submit">提交</button>

六、全局注册 ​

ts
// src/main.ts
import { permission } from '@/directives/permission'
import { copy } from '@/directives/copy'

app.directive('permission', permission)
app.directive('copy', copy)

⚠️ 坑 2:全局指令名字会自动加 v- 前缀,不要写成 v-permission。

七、局部注册 ​

vue
<script setup lang="ts">
const vFocus = {
  mounted: (el: HTMLElement) => el.focus(),
}
</script>

<template>
  <input v-focus />
</template>

八、修饰符与参数 ​

ts
binding.arg       // '500'
binding.modifiers // { stop: true }
binding.value     // 表达式值
vue
<div v-tooltip:foo.stop="'提示'"></div>

九、本章小结 ​

钩子用途
mountedDOM 已挂载,绑定事件
updated数据更新后改 DOM
unmounted清理工作
binding.value指令表达式值
app.directive()全局注册

动手练习 ​

  1. v-lazy:用 IntersectionObserver 实现图片懒加载
  2. v-truncate:超长文本截断,加省略号
  3. v-tooltip:鼠标悬浮显示自定义提示

下一章:第 16 章:动画 →

本站基于 VitePress 构建 · 由 StackHub 团队维护