Creating and Using Custom Directives in Vue 3: Lifecycle Hooks and Practical Implementations

This article explains what Vue directives are, how to register them globally or locally, details the Vue 3 directive lifecycle hooks, and provides step‑by‑step code examples for building custom directives such as copy, click‑outside, focus, and debounce to enhance frontend functionality.

360 Quality & Efficiency
360 Quality & Efficiency
360 Quality & Efficiency
Creating and Using Custom Directives in Vue 3: Lifecycle Hooks and Practical Implementations

In Vue, a directive is the bridge between the DOM and logical behavior, essentially an independent function bound to a DOM element; core directives like v-model and v-show are built‑in, while developers can register custom directives globally with Vue.directive(id, definition) or locally within components.

Vue 3 provides several lifecycle hooks for directives: created (called before the element’s attribute or event listener is applied), beforeMount (when the directive is first bound before the parent component mounts), mounted (after the parent component mounts), beforeUpdate (before the VNode updates), updated (after the VNode and its children update), beforeUnmount (before the parent component unmounts), and unmounted (once the directive is detached and the parent component is removed).

To implement custom directives, first create a folder under src (e.g., Directives) and import it in main.js:

import Directives from "@/Directives/index";
const app = createApp(App);
app.use(Directives);
app.mount("#app");

The index.js file aggregates individual directive modules:

import copy from "./copy"; // custom copy directive
import clickoutside from "./clickoutside";
import focus from "./focus";
import debounce from "./debounce";

const directivesList = { copy, clickoutside, focus, debounce };

const directives = {
  install(app) {
    Object.keys(directivesList).forEach(key => {
      app.directive(key, directivesList[key]); // register each directive
    });
  }
};
export default directives;

The copy.js directive handles clipboard copying with validation and user feedback using Element‑Plus messages, binding a click handler on mounted, updating the stored value on beforeUpdate, and cleaning up on unmounted:

import { ElMessage } from "element-plus";
const copy = {
  mounted(el, { value }) {
    el.$value = value;
    el.handler = () => {
      if (!el.$value) {
        ElMessage.warning({ message: "复制的值不能为空。", type: "warning" });
        return;
      }
      if (window.clipboardData) {
        window.clipboardData.setData("text", el.$value);
      } else {
        (function(content) {
          document.oncopy = function(e) {
            e.clipboardData.setData("text", content);
            e.preventDefault();
            document.oncopy = null;
          };
        })(el.$value);
        document.execCommand("Copy");
      }
      ElMessage.success("复制成功");
    };
    el.addEventListener("click", el.handler);
  },
  beforeUpdate(el, { value }) {
    el.$value = value;
  },
  unmounted(el) {
    el.removeEventListener("click", el.handler);
  }
};
export default copy;

The clickoutside.js directive detects clicks outside the bound element and optionally calls a provided handler:

const clickoutside = {
  beforeMount(el, binding) {
    function documentHandler(e) {
      if (el.contains(e.target)) return false;
      if (binding.value && typeof binding.value === "function") {
        binding.value(e);
      }
    }
    el.__vueClickOutside__ = documentHandler;
    document.addEventListener("click", documentHandler);
  },
  beforeUnmount(el) {
    document.removeEventListener("click", el.__vueClickOutside__);
    delete el.__vueClickOutside__;
  }
};
export default clickoutside;

The focus.js directive simply focuses the first input inside the element when it is mounted:

const focus = {
  mounted(el) {
    el.querySelector("input").focus();
  }
};
export default focus;

The debounce.js directive debounces a provided function, waiting one second after the last keyup event before invoking it:

const debounce = {
  inserted(el, binding) {
    let timer;
    el.addEventListener('keyup', () => {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        binding.value();
      }, 1000);
    });
  }
};
export default debounce;

Overall, custom directives in Vue serve not only to encapsulate reusable DOM‑related logic but also to integrate third‑party plugins without rewriting them, offering a flexible way to extend frontend frameworks and streamline component development.

Vue3custom-directivesdirective-lifecycle
360 Quality & Efficiency
Written by

360 Quality & Efficiency

360 Quality & Efficiency focuses on seamlessly integrating quality and efficiency in R&D, sharing 360’s internal best practices with industry peers to foster collaboration among Chinese enterprises and drive greater efficiency value.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.