← 返回题目列表

Vue 自定义指令是什么?和组件、组合式函数怎么区分?

高频 中等 第 8 / 27 题 更新于 2026/07/29
Vue自定义指令directiveDOM

简化版

自定义指令用于封装可复用的底层 DOM 行为,比如自动聚焦、权限控制、点击外部关闭、懒加载。组件封装 UI 和状态,组合式函数封装响应式逻辑,自定义指令更适合直接操作 DOM 的场景。

详细版

自定义指令示例:

const vFocus = {
  mounted(el: HTMLInputElement) {
    el.focus();
  },
};

使用:

<input v-focus />

它的钩子和元素生命周期相关,例如 mountedupdatedunmounted。面试重点是:不要滥用指令处理业务状态;需要 UI 结构用组件,需要逻辑复用用 composable,需要 DOM 行为复用才考虑指令。

完整版教学

一、自定义指令解决可复用 DOM 行为

Vue 组件主要关心声明式渲染,但真实项目仍会遇到一些直接 DOM 行为。 例如输入框自动聚焦、滚动加载、点击外部关闭、图片懒加载、权限按钮隐藏。 这些行为如果散落在组件里,会重复且难维护。

<input v-focus />
<button v-permission="'admin'">删除</button>

自定义指令把这些 DOM 行为封装成模板上的指令。 调用方只看模板就知道这个元素有什么额外行为。 这就是它比在 mounted 里手写 DOM 操作更清晰的地方。

二、指令钩子围绕元素生命周期执行

Vue3 自定义指令常见钩子包括 createdbeforeMountmountedbeforeUpdateupdatedbeforeUnmountunmounted。 实际最常用的是 mountedunmounted。 前者绑定 DOM 行为,后者清理事件监听。

const vClickOutside = {
  mounted(el: HTMLElement, binding: any) {
    const handler = (event: MouseEvent) => {
      if (!el.contains(event.target as Node)) {
        binding.value(event);
      }
    };
    document.addEventListener('click', handler);
    (el as any).__clickOutside__ = handler;
  },
  unmounted(el: HTMLElement) {
    document.removeEventListener('click', (el as any).__clickOutside__);
  },
};

如果页面有 10 个下拉框,这个指令能避免写 10 份 document 点击监听。 同时 unmounted 清理监听,能减少内存泄漏风险。

三、binding 提供指令参数和值

指令钩子的第二个参数是 binding。 它包含 valueoldValueargmodifiers 等信息。 这让一个指令可以根据参数和修饰符调整行为。

<button v-permission="'admin'">删除</button>
<img v-lazy:src.once="url" />
字段含义示例
value指令绑定值'admin'
oldValue更新前的值上一次权限
arg指令参数src
modifiers修饰符对象{ once: true }

这些信息足够覆盖很多 DOM 行为配置。 但如果配置越来越复杂,可能说明它应该升级成组件。

四、指令和组件的边界要分清

组件适合封装结构、样式、状态和交互。 指令适合给已有元素附加低层 DOM 行为。 如果一个能力需要渲染额外 DOM、管理内部状态、暴露多个事件,通常应该写组件。

适合指令:
  focus、click-outside、lazy、permission
适合组件:
  Modal、Select、Table、Upload

例如弹窗不适合写成一个 v-modal 指令。 弹窗有遮罩、内容区、动画、插槽、关闭状态,明显是组件职责。 但“点击弹窗外部关闭”可以是指令。

五、指令和组合式函数的边界也不同

组合式函数封装响应式状态和逻辑。 自定义指令封装模板元素上的 DOM 行为。 两者有时可以配合,但不是同一类抽象。

function usePermission() {
  const roles = ref(['admin']);
  const hasRole = (role: string) => roles.value.includes(role);
  return { roles, hasRole };
}

usePermission 适合在 JS 逻辑里判断权限。 v-permission 适合在模板上快速控制按钮显示。 一个管逻辑,一个管 DOM 表现,边界清楚才好维护。

六、自定义指令要注意 SSR 和清理

指令经常操作 windowdocumentel。 如果项目有 SSR,这些对象在服务端不存在,需要延迟到客户端钩子里执行。 此外,绑定事件、Observer、定时器后必须清理。

mounted:
  addEventListener / observe / setInterval
unmounted:
  removeEventListener / disconnect / clearInterval

记忆钩子:组件封装“东西”,组合式函数封装“逻辑”,指令封装“贴在 DOM 上的行为”。

这个边界能帮你避免把所有复用都写成指令。 面试官问“什么时候不用指令”时,就按这个边界回答。

七、常见误区与追问

  • 误区:自定义指令可以替代组件。 指令适合 DOM 行为,复杂 UI 结构和状态应该用组件。
  • 误区:指令里绑定事件不用清理。 指令如果注册全局事件、Observer 或定时器,必须在卸载时清理。
  • 误区:权限指令就能保证安全。 前端权限指令只能控制展示,真正权限必须由后端接口校验。
  • 追问:自定义指令有哪些常见场景? 自动聚焦、点击外部、图片懒加载、权限展示、拖拽、埋点曝光等。
  • 追问:指令怎么接收参数? 通过 binding 的 valueargmodifiers 等字段接收配置。
  • 追问:SSR 项目写指令要注意什么? 避免在服务端访问 window/document,DOM 操作放到客户端挂载阶段。

八、加强记忆

自定义指令这题用“DOM 行为复用”来记。它不是组件,也不是状态逻辑工具;组件封装 UI,composable 封装响应式逻辑,directive 封装元素行为。回答时用 v-focusv-click-outside 举例,再补清理事件和权限指令不等于安全。