Vue 中自定义指令如何编写?有哪些常用场景?

自定义指令

// 全局注册
const app = createApp(App)

app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})

// 局部注册
<script setup>
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<!-- 使用 -->
<input v-focus />

指令钩子

app.directive('my-directive', {
  // 元素挂载前
  created(el, binding, vnode) {},
  // 元素挂载前(CSS 已确定)
  beforeMount(el, binding) {},
  // 挂载后
  mounted(el, binding) {},
  // 更新前
  beforeUpdate(el, binding) {},
  // 更新后
  updated(el, binding) {},
  // 卸载前
  beforeUnmount(el, binding) {},
  // 卸载后
  unmounted(el, binding) {}
})

binding 对象

{
  value: '传递的值',       // v-my="value"
  oldValue: '旧值',         // 更新时的旧值
  arg: 'foo',              // v-my:foo
  modifiers: { lazy: true }, // v-my.lazy
  dir: { /* 指令定义对象 */ }
}

常用自定义指令示例

1. v-loading(加载中)

app.directive('loading', {
  mounted(el, binding) {
    el.style.position = 'relative'
    const mask = document.createElement('div')
    mask.className = 'loading-mask'
    mask.style.display = binding.value ? 'flex' : 'none'
    el.appendChild(mask)
    el._loadingMask = mask
  },
  updated(el, binding) {
    if (el._loadingMask) {
      el._loadingMask.style.display = binding.value ? 'flex' : 'none'
    }
  }
})

2. v-debounce(防抖点击)

app.directive('debounce', {
  mounted(el, binding) {
    let timer = null
    el.addEventListener('click', () => {
      clearTimeout(timer)
      timer = setTimeout(() => {
        binding.value()
      }, 500)
    })
  }
})
// <button v-debounce="handleClick">Click</button>

3. v-permission(权限控制)

app.directive('permission', {
  mounted(el, binding) {
    if (!hasPermission(binding.value)) {
      el.parentNode?.removeChild(el)
    }
  }
})
// <button v-permission="'admin'">删除</button>

4. v-copy(复制到剪贴板)

app.directive('copy', {
  mounted(el, binding) {
    el.style.cursor = 'pointer'
    el.addEventListener('click', () => {
      navigator.clipboard.writeText(binding.value)
      showToast('已复制')
    })
  }
})
// <span v-copy="text">{{ text }}</span>

5. v-lazy(图片懒加载)

app.directive('lazy', {
  mounted(el, binding) {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        el.src = binding.value
        observer.unobserve(el)
      }
    })
    observer.observe(el)
    el._observer = observer
  },
  unmounted(el) {
    el._observer?.disconnect()
  }
})
// <img v-lazy="imageSrc" />

Vue3 变化

同分类其他题目