Vue.js: التوجيهات المخصصة

آخر تحديث: 2026-08-26

تتيح لك التوجيهات المخصصة توسيع نطاق صيغة template في Vue — باستخدام سمات خاصة تبدأ بـ v- للتعامل مباشرةً مع DOM الأساسي. يأتي Vue مزودًا بتوجيهات مدمجة مثل v-if وv-for وv-model؛ ويمكنك إنشاء توجيهات خاصة بك مثل v-focus وv-permission وv-debounce.

تُعد التوجيهات المخصصة وسيلة فعالة لكتابة «أدوات DOM من المستوى المنخفض» — فهي تُغلف عمليات DOM القابلة لإعادة الاستخدام في صيغة إعلانية. ويتيح لك فهم «نقاط ربط دورة الحياة» الخمس الرئيسية كتابة جميع أنواع التوجيهات v.

1. ما ستتعلمه



2. كابوس زر «الإذن» «المكرر في 5 أماكن»

(1) المشكلة: تتطلب جميع الأزرار الخمسة إجراء فحوصات للأذونات

كان لدى المسؤول عن حساب أليس 5 أزرار تتطلب التحقق من الصلاحيات:

VUE
<!-- ❌ The "Broken" Version:5 a button,5 Permission Code -->
<template>
  <button v-if="hasPermission('user.create')" @click="createUser">Create</button>
  <button v-if="hasPermission('user.delete')" @click="deleteUser">Delete</button>
  <button v-if="hasPermission('user.edit')" @click="editUser">Edit</button>
  <button v-if="hasPermission('order.create')" @click="createOrder">Create Order</button>
  <button v-if="hasPermission('order.cancel')" @click="cancelOrder">Cancel</button>
</template>

<script setup>
function hasPermission(perm) {
  return user.value.permissions?.includes(perm)
}
</script>

5 أزرار × 5 عمليات التحقق من الأذونات = 25 سطراً من التعليمات البرمجية المتكررة. تتطلب إضافة زر جديد كتابة v-if آخر.

مدير المنتج تشارلي:

"أليس، لدينا أكثر من 50 زرًا في لوحة الإدارة. نحتاج إلى توجيه 'v-permission' لجعل الأمر أكثر تنظيمًا."

(2) الحل باستخدام توجيه Vue مخصص: 1 v-permission

JS
// directives/permission.js
export const permission = {
  mounted(el, binding) {
    const { value } = binding  // 'user.create'
    const userPermissions = getCurrentUser().permissions || []
    
    if (!userPermissions.includes(value)) {
      el.parentNode?.removeChild(el)  // If you don't have permission, remove it.
    }
  }
}
JS
// main.js
import { permission } from './directives/permission'
app.directive('permission', permission)
VUE
<!-- Usage: 1 v-permission replaces all v-if -->
<template>
  <button v-permission="'user.create'" @click="createUser">Create</button>
  <button v-permission="'user.delete'" @click="deleteUser">Delete</button>
  <button v-permission="'order.create'" @click="createOrder">Create Order</button>
</template>

50 زرًا، 50 v-permission — أكثر إيجازًا بنسبة 50% من 50 v-if.

(3) الإيرادات

بعد التوجيهات المخصصة:



3. أساسيات الأوامر المخصصة

(1) 3 طرق للتسجيل

JS
// 1. Global Commands(main.js)
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// Global Registration:All components are available v-focus
app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})

app.mount('#app')
VUE
<!-- Local Instructions(Recommendations) -->
<!-- src/components/Input.vue -->
<script setup>
// Local Registration:Only this component works
const vFocus = {
  mounted(el) {
    el.focus()
  }
}
</script>

<template>
  <input v-focus>
</template>
JS
// 2. Abbreviation(mounted + updated)
app.directive('color', (el, binding) => {
  el.style.color = binding.value
})

(2) النقاط الخمس الرئيسية في دورة الحياة

JS
app.directive('demo', {
  // 1. created(Command Creation)
  created(el, binding) {
    console.log('1. Command Creation')
  },
  
  // 2. beforeMount(Before mounting the element)
  beforeMount(el) {
    console.log('2. Before Mounting')
  },
  
  // 3. mounted(The element has been mounted)⭐ Most Commonly Used
  mounted(el, binding) {
    console.log('3. Mounted')
  },
  
  // 4. beforeUpdate(Before the dependency update)
  beforeUpdate(el, binding) {
    console.log('4. Before the update')
  },
  
  // 5. updated(After the dependency update)
  updated(el, binding) {
    console.log('5. Updated')
  },
  
  // 6. beforeUnmount(Before Uninstalling)
  beforeUnmount(el) {
    console.log('6. Before Uninstalling')
  },
  
  // 7. unmounted(After uninstallation)⭐ For cleaning
  unmounted(el) {
    console.log('7. Uninstalled')
  }
})

(3) شرح مفصل لمعلمات Hook

JS
// el, binding, vnode, prevVnode 4 parameter
mounted(el, binding, vnode, prevVnode) {
  // el: Elements Bound to Commands
  el.style.color = 'red'
  
  // binding: Instruction Information Object
  binding.value    // Instruction Value, e.g. v-foo="bar" → bar
  binding.arg      // Parameters, e.g. v-foo:arg → 'arg'
  binding.modifiers  // Modifiers, e.g. v-foo.bar → { bar: true }
  binding.instance  // Component Instances That Use Commands
  binding.dir       // Instruction-Defined Objects
  
  // vnode: Vue Virtual Node(Generally not used)
  // prevVnode: Previous Virtual Node
}


4. 5 أوامر أساسية للاستخدام العملي

(1) v-focus: التركيز التلقائي

JS
// directives/focus.js
export const focus = {
  mounted(el, binding) {
    if (binding.value !== false) {
      el.focus()
    }
  }
}
VUE
<template>
  <!-- 1. Autofocus -->
  <input v-focus>
  
  <!-- 2. Focus on Conditions -->
  <input v-focus="shouldFocus">
  
  <!-- 3. Delayed Focus -->
  <input v-focus:delay="500">
</template>

(2) v-permission: التحكم في الأذونات

JS
// directives/permission.js
import { getCurrentUser } from '@/utils/auth'

export const permission = {
  mounted(el, binding) {
    const { value, modifiers } = binding
    const user = getCurrentUser()
    
    // value: String 'user.create' or Array ['user.create', 'user.delete']
    // modifiers.disable: Disable, not remove
    const required = Array.isArray(value) ? value : [value]
    const hasPermission = required.every(p => 
      user.permissions?.includes(p)
    )
    
    if (!hasPermission) {
      if (modifiers.disable) {
        el.disabled = true
        el.title = 'No permission'
      } else {
        el.parentNode?.removeChild(el)
      }
    }
  }
}
VUE
<template>
  <!-- Single Permission -->
  <button v-permission="'user.create'">Create</button>
  
  <!-- Multiple Permissions(All met)-->
  <button v-permission="['user.read', 'user.write']">Edit</button>
  
  <!-- Disable when permissions are lacking(rather than removing)-->
  <button v-permission.disable="'user.delete'">Delete</button>
</template>

(3) v-debounce: منع الارتداد في الأحداث

JS
// directives/debounce.js
export const debounce = {
  mounted(el, binding) {
    const { value, arg = 300 } = binding
    
    if (typeof value !== 'function') {
      console.warn('v-debounce: value must be a function')
      return
    }
    
    let timer = null
    el.__debounceTimer__ = timer
    
    el.addEventListener('click', () => {
      clearTimeout(timer)
      timer = setTimeout(() => value(), arg)
      el.__debounceTimer__ = timer
    })
  },
  
  unmounted(el) {
    if (el.__debounceTimer__) {
      clearTimeout(el.__debounceTimer__)
    }
  }
}
VUE
<template>
  <button v-debounce="handleClick" v-debounce:500="handleClick">Click me</button>
  <input v-debounce="handleInput" v-debounce:1000="handleInput">
</template>

<script setup>
function handleClick() {
  console.log('Clicked (debounced 500ms)')
}
</script>

(4) v-copy: انقر للنسخ

JS
// directives/copy.js
export const copy = {
  mounted(el, binding) {
    el.addEventListener('click', async () => {
      try {
        await navigator.clipboard.writeText(binding.value)
        const original = el.textContent
        el.textContent = 'Copied!'
        setTimeout(() => { el.textContent = original }, 1500)
      } catch (err) {
        console.error('Copy failed:', err)
      }
    })
  }
}
VUE
<template>
  <button v-copy="shareUrl">Copy Link</button>
  <code v-copy="apiKey">Click to copy</code>
</template>

(5) v-lazy-load: التحميل المؤجل للصور

JS
// directives/lazyLoad.js
export const lazyLoad = {
  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()
  }
}
VUE
<template>
  <img v-lazy-load="imageUrl" alt="...">
</template>


5. معلمات الأوامر ومعدِّلاتها وقيمها

(1) ثلاثة أنواع من الأوامر

VUE
<!-- 1. v-directive="value" (value) -->
<input v-foo="username">

<!-- 2. v-directive:arg(Parameters,Fixed String) -->
<input v-foo:delay="500">

<!-- 3. v-directive.modifier(Modifiers,Boolean objects) -->
<input v-foo.bar>

<!-- 4. Combination -->
<input v-foo:delay.bar="500">

(2) طرق استقبال JS

JS
app.directive('demo', (el, binding) => {
  // v-demo="123"
  binding.value  // 123
  
  // v-demo:abc
  binding.arg  // 'abc'
  
  // v-demo.foo
  binding.modifiers  // { foo: true }
  
  // v-demo:abc.foo="123"
  binding.value    // 123
  binding.arg       // 'abc'
  binding.modifiers // { foo: true }
})

(3) 5 سيناريوهات تركيبية رئيسية

VUE
<!-- Scene 1:v-permission:disable -->
<button v-permission:disable="'user.create'">
  <!-- arg='disable', value='user.create' -->
</button>

<!-- Scene 2:v-debounce:500 -->
<button v-debounce:500="handler">
  <!-- arg='500'(500ms Image Stabilization) -->
</button>

<!-- Scene 3:v-once.lazy -->
<img v-once.lazy="imageUrl">
<!-- modifiers.lazy=true, value=imageUrl -->
</template>


6. مثال كامل: 5 أوامر رئيسية + تدريب عملي

▶ مثال: 1. التنفيذ الكامل لـ v-focus

JS
export const focus = {
  mounted(el, binding) {
    if (binding.value === false) return
    if (binding.arg) {
      setTimeout(() => el.focus(), parseInt(binding.arg))
    } else {
      el.focus()
    }
  }
}
▶ جرّب الكود

▶ مثال: 2. التنفيذ الكامل لـ v-permission

JS
import { getCurrentUser } from '@/utils/auth'

export const permission = {
  mounted(el, binding) {
    const { value, modifiers } = binding
    const user = getCurrentUser()
    const required = Array.isArray(value) ? value : [value]
    const ok = required.every(p => user.permissions?.includes(p))
    
    if (!ok) {
      if (modifiers.disable) {
        el.disabled = true
        el.style.opacity = '0.5'
        el.title = 'No permission'
      } else {
        el.parentNode?.removeChild(el)
      }
    }
  },
  
  updated(el, binding) {
    // Permissions may change(User Role Switching),Re-examine
    this.mounted(el, binding)
  }
}
▶ جرّب الكود

▶ مثال: 3. التنفيذ الكامل لـ v-debounce

JS
export const debounce = {
  mounted(el, binding) {
    const fn = binding.value
    const delay = parseInt(binding.arg) || 300
    
    if (typeof fn !== 'function') {
      console.warn('[v-debounce] value must be a function')
      return
    }
    
    let timer = null
    el.addEventListener('click', () => {
      clearTimeout(timer)
      timer = setTimeout(() => fn(), delay)
    })
    el._debounceTimer = timer
  },
  
  unmounted(el) {
    if (el._debounceTimer) clearTimeout(el._debounceTimer)
  }
}
▶ جرّب الكود

▶ مثال: 4. مرجع سريع لـ 5 أخطاء شائعة

الخطأ الأعراض الحل
اسم التوجيه بدون "v-" لا يعمل v-focus (وليس focus)
القيمة ليست دالة لا يتم التنفيذ تحقق من v-debounce="handler"
تنظيف الموارد غير المثبتة تسربات الذاكرة تنظيف المؤقتات/المراقبين
تجاوز التوجيهات المدمجة في Vue خطأ الاسم غير "v-if"، إلخ
لا يتم التعرف على تغييرات قيم الأوامر تم التثبيت مرة واحدة فقط استخدام خطاف updated

▶ مثال: 5. مقارنة بين مؤشرات الأداء الخمسة الرئيسية

التنفيذ قابلية إعادة الاستخدام الأداء قابلية التطبيق
v-if + وظائف مساعدة ⭐⭐⭐ لمرة واحدة
الأوامر المخصصة ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ أدوات DOM
المكونات العالمية ⭐⭐⭐ ⭐⭐⭐ واجهة مستخدم معقدة
قابل للتركيب ⭐⭐⭐⭐ ⭐⭐⭐⭐ منطق الأعمال
بينيا ⭐⭐⭐⭐ ⭐⭐⭐ الوضع العالمي

▶ مثال: 6. 5 نصائح لاستخدام الأوامر المدمجة

JS
// Draw on Vue Implementation of Built-in Instructions
import { createApp } from 'vue'

const app = createApp({})

// 1. v-show(Control display)
app.directive('show', {
  mounted(el, binding) { el.style.display = binding.value ? '' : 'none' },
  updated(el, binding) { el.style.display = binding.value ? '' : 'none' }
})

// 2. v-text(Settings textContent)
app.directive('text', {
  mounted(el, binding) { el.textContent = binding.value },
  updated(el, binding) { el.textContent = binding.value }
})

// 3. v-html(Settings innerHTML,XSS Risks)
app.directive('html', {
  mounted(el, binding) { el.innerHTML = binding.value },
  updated(el, binding) { el.innerHTML = binding.value }
})

// 4. v-once(Render only once)
app.directive('once', {
  mounted(el, binding, vnode) {
    if (binding.value !== indefinido) {
      vnode.context[binding.arg] = binding.value
    }
  }
})
▶ جرّب الكود

❓ أسئلة شائعة

س كيف أختار بين التوجيهات المخصصة والمكونات؟
ج استخدم التوجيهات لعمليات DOM ذات المستوى المنخفض (مثل التركيز والتمرير والكانفاس). واستخدم المكونات لواجهات المستخدم المعقدة (التي تتضمن الstatus والأحداث والبيانات). التوجيهات لا تحتفظ بstatus، بينما المكونات تحتفظ بstatus.
س هل value الخاص بأحد التوجيهات تفاعلي؟
ج تؤدي التغييرات التي تطرأ على value إلى تشغيل المرفق updated. للاستماع إلى التغييرات التي تطرأ على value، استخدم watch(binding.value) أو قارن بين القيم القديمة والجديدة داخل المرفق updated.
س كيف تكتب v-permission:disable؟
ج arg='disable'، modifiers.disable=true. تحدد التوجيهية binding.modifiers.disable ما إذا كان سيتم تعطيلها أو إزالتها.
س هل يمكن استخدام TypeScript في التوجيهات؟
ج نعم. يدعم Vue 3.3+ defineDirective والأنواع العامة في TypeScript. ومع ذلك، عادةً ما يكون استخدام كائنات JavaScript أبسط.
س كيف أختار بين التوجيهات العامة والمحلية؟
ج استخدم التوجيهات المحلية للميزات الخاصة بالأعمال (v-permission). واستخدم التوجيهات العامة للأدوات ذات الأغراض العامة (v-focus / v-copy). يوصي هذا الدليل التعليمي باستخدام التوجيهات المحلية (لأنها أسهل في الصيانة).
س هل يمكن لأمر ما أن يمرر مرجعًا؟
ج نعم. <input v-my-directive="myRef" />، حيث يمثل binding.value في الأمر كائن المرجع. ومع ذلك، لا يُنصح بذلك (نمط غير مرغوب فيه).
س هل تحتوي مكتبة VueUse على توجيهات؟
ج نعم. vFocus / vClickOutside / vLazyLoad / vInfiniteScroll وغيرها هي جميعها توجيهات توفرها مكتبة VueUse. من الأفضل استخدام مكتبة VueUse بدلاً من كتابة توجيهات خاصة بك.

📖 ملخص


📝 تمارين

  1. أسئلة أساسية (مستوى الصعوبة: ⭐)

    تنفيذ التوجيه v-focus:

    • يقبل المعلمة arg (التأخير بالمللي ثانية)
    • يقبل المُعدِّلات (prevent: يمنع السلوك الافتراضي)
  2. مسائل متقدمة (مستوى الصعوبة: ⭐⭐)

    تنفيذ النسخة الكاملة من v-permission:

    • يدعم سلاسل الأذونات الفردية: v-permission="'user.create'"
    • يدعم مصفوفات الأذونات: v-permission="['user.read', 'user.write']"
    • المُعدِّل المدعوم: .disable (يعطل بدلاً من الإزالة)
    • باستخدام currentUser (توفير/إدخال Injection)
  3. مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)

    تنفيذ «مجموعة أوامر» كاملة:

    1. 5 توجيهات: v-focus / v-permission / v-debounce / v-copy / v-lazy-load
    2. التنفيذ الكامل لكل تعليمات + أنواع TypeScript
    3. تصدير كل شيء من directives/index.js
    4. 5 حالات اختبار (باستخدام Vitest)
    5. الاستخدام في سيناريوهات واقعية (5 سيناريوهات مختلفة في البنية الخلفية للتجارة الإلكترونية)
    6. توثيق واجهة برمجة التطبيقات (API) والمعلمات الخاصة بكل أمر
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%