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

آخر تحديث: 2026-10-20

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

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

التوجيهات ذات صلة:app.directive() v-model v-for v-if

1. ما ستتعلمه


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

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

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

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)

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

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

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


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

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

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
}

▶ مثال: 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 !== undefined) {
      vnode.context[binding.arg] = binding.value
    }
  }
})
▶ جرّب الكود

الإخراج:

TEXT
بعد تسجيل التوجيهات على app: v-show يضبط display وv-text يضبط textContent
v-html يضبط innerHTML، وv-once ينسخ القيمة إلى vnode.context مرة واحدة عند mounted

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

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

JS
// directives/focus.js
export const focus = {
  mounted(el, binding) {
    if (binding.value !== false) {
      el.focus()
    }
  }
}

(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)
      }
    }
  }
}

(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__)
    }
  }
}

(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)
      }
    })
  }
}

(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()
  }
}

▶ مثال: التنفيذ الكامل لـ 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()
    }
  }
}
▶ جرّب الكود

الإخراج:

TEXT
عند mount يحصل العنصر على التركيز فورًا
v-focus:500 يؤخر التركيز 500ms، وv-focus="false" يمنع التركيز تمامًا

▶ مثال: التنفيذ الكامل لـ 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)
  }
}
▶ جرّب الكود

الإخراج:

TEXT
زر يملك الصلاحية → يظهر كما هو دون تغيير
زر بلا صلاحية → يُزال من DOM، ومع v-permission.disable يُعطَّل ويصبح opacity 0.5 مع title توضيحي

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. تمرين شامل: تنفيذ التوجيه v-debounce بشكل كامل

▶ مثال: التنفيذ الكامل لـ 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)
  }
}
▶ جرّب الكود

الإخراج:

TEXT
النقر مرة واحدة → لا يُنفَّذ المعالج فورًا بل بعد 300ms (أو المدة في v-debounce:500)
النقرات المتتابعة تلغي المؤقت السابق فلا يُنفَّذ إلا آخر نقرة
عند إزالة المكوّن يُلغى المؤقت في unmounted بلا تنفيذ

(1) 📊 4. مرجع سريع لـ 5 أخطاء شائعة

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

(2) 📊 5. مقارنة بين مؤشرات الأداء الخمسة الرئيسية

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

التوجيهات ذات صلة:app.directive() v-model v-for v-if

❓ أسئلة شائعة

س كيف أختار بين التوجيهات المخصصة والمكونات؟
ج استخدم التوجيهات لعمليات 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%