Vue.js: Refs و DOM

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

تتيح لك مراجع القوالب الوصول مباشرةً إلى عناصر DOM أو مثيلات المكونات الفرعية — على سبيل المثال، استدعاء input.focus() أو الوصول إلى أساليب المكونات. وقد أدخلت Vue 3.5 واجهة برمجة تطبيقات التركيب useTemplateRef الأكثر قوة، والتي تعمل بالاقتران مع استدلال الأنواع في TypeScript.

تُعد «Template Refs» «الملاذ الأخير» — توصي Vue باستخدام «refs» و«reactive» و«props» و«emit» لحل معظم المشكلات؛ ولا ينبغي استخدام «Template Refs» إلا عندما تحتاج إلى التعامل مباشرةً مع DOM أو مثيل مكون.

1. ما ستتعلمه



2. معضلة «التركيز التلقائي» في نموذج تسجيل الدخول

(1) المشكلة: كيف يمكن أن يحصل حقل الإدخال على التركيز تلقائيًّا عند فتح النافذة المنبثقة؟

قامت أليس بإنشاء نافذة منبثقة لتسجيل الدخول من المفترض أن تركز تلقائيًا على حقل إدخال اسم المستخدم:

JS
// ❌ The "Broken" Version:Directly querySelector
onMounted(() => {
  const input = document.querySelector('.username-input')
  input.focus()  // ❌ Does not meet the requirements Vue Philosophy
})

فلسفة Vue: تجنب التلاعب المباشر بـ DOM. استخدم «Template Refs» بدلاً من ذلك.

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

"أليس، عندما يُفتح النافذة المنبثقة، يجب أن يتم تحديد حقل اسم المستخدم تلقائيًّا حتى يتمكن المستخدمون من البدء في الكتابة على الفور."

(2) عرض حل «مراجع القوالب»

VUE
<template>
  <!-- ref="usernameInput" Mark this element -->
  <input ref="usernameInput" type="text" class="username-input">
</template>

<script setup>
import { ref, onMounted } from 'vue'

// 1. Create ref Variable(The name must match the template ref Match)
const usernameInput = ref(null)

onMounted(() => {
  // 2. DOM Ready,Visit input Element
  usernameInput.value.focus()
})
</script>
VUE
<!-- Pop-up Scenarios:Click the button to open modal,Automatic focus -->
<template>
  <button @click="showModal = true">Login</button>
  
  <Modal v-if="showModal" @close="showModal = false">
    <input ref="usernameInput" type="text">
  </Modal>
</template>

<script setup>
import { ref, nextTick } from 'vue'

const showModal = ref(false)
const usernameInput = ref(null)

async function openModal() {
  showModal.value = true
  // ✅ Wait for DOM update, then focus
  await nextTick()
  usernameInput.value.focus()
}
</script>

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

بعد استخدام «Template Refs»:



3. الاستخدام الأساسي لـ ref

(1) مرجع السلسلة (بأسلوب Vue 2، تم إهماله)

JS
// ❌ Not recommended:String ref
export default {
  mounted() {
    this.$refs.input.focus()
  }
}

(2) المتغير ref (موصى به في Vue 3)

VUE
<template>
  <input ref="inputRef">
</template>

<script setup>
import { ref, onMounted } from 'vue'

const inputRef = ref(null)

onMounted(() => {
  console.log(inputRef.value)  // <input> DOM Element
  inputRef.value.focus()        // Call DOM API
})
</script>

(3) 5 عمليات أساسية

JS
// 1. Visit DOM Element
inputRef.value  // <input> Element

// 2. Call DOM API
inputRef.value.focus()
inputRef.value.blur()
inputRef.value.select()
inputRef.value.scrollIntoView()

// 3. Read/Edit DOM Properties
inputRef.value.value  // input value
inputRef.value.disabled  // disabled Properties
inputRef.value.style.color = 'red'  // Edit Style

// 4. Monitoring DOM Events (Not recommended, use @event)
inputRef.value.addEventListener('focus', handler)

// 5. Accessing a Child Component Instance(For more details, see 17.4)
childRef.value.someMethod()


4. useTemplateRef (موصى به لـ Vue 3.5 وما فوق)

(1) لماذا نحتاج إلى useTemplateRef؟

يتطلب المتغير ref في <script setup> اسمين (القالب ref والمتغير)، مما قد يؤدي بسهولة إلى حدوث تناقضات. أما useTemplateRef فيعالج هذه المشكلة باستخدام اسم واحد، كما أن استدلال الأنواع في TypeScript أكثر قوة.

VUE
<template>
  <input ref="usernameInput">
</template>

<script setup>
import { useTemplateRef, onMounted } from 'vue'

// ✅ One Name Does It All(Vue 3.5+)
const inputRef = useTemplateRef('usernameInput')

onMounted(() => {
  inputRef.value.focus()  // Type automatically inferred as HTMLInputElement
})
</script>

(2) 5 مزايا رئيسية

الميزة الوصف
استدلال النوع يتعرف TypeScript عليه تلقائيًا على أنه عنصر HTMLInputElement
إعادة التسمية الآمنة قم بتغيير الاسم في مكان واحد؛ وسيقوم IDE بمزامنته
تجنب عدم تطابق الأسماء تحدث الأخطاء عندما لا تتطابق مراجع القوالب وأسماء المتغيرات
إعداد مبسط لا يلزم وجود const inputRef = ref(null)
أدوات التطوير المحسّنة دعم Vue DevTools 5.x

(3) مقارنة شاملة

VUE
<!-- Old notation:Variable ref -->
<template>
  <input ref="usernameInput">
</template>

<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null)  // The name may not match the template.
onMounted(() => inputRef.value.focus())
</script>

<!-- New Writing Style:useTemplateRef(Vue 3.5+ Recommendations)-->
<template>
  <input ref="usernameInput">
</template>

<script setup>
import { useTemplateRef, onMounted } from 'vue'
const inputRef = useTemplateRef('usernameInput')  // One Name Does It All
onMounted(() => inputRef.value.focus())
</script>


5. الوصول إلى مثيل مكون تابع

(1) defineExpose: طريقة Expose

VUE
<!-- Child component:MyInput.vue -->
<template>
  <input ref="inputRef" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
</template>

<script setup>
import { ref } from 'vue'

const props = defineProps({ modelValue: String })
const inputRef = ref(null)

// Exposed for use by the parent component
defineExpose({
  focus: () => inputRef.value?.focus(),
  select: () => inputRef.value?.select(),
  clear: () => { inputRef.value.value = '' }
})
</script>

(2) الوصول إلى المكون الأصلي

VUE
<template>
  <MyInput ref="myInputRef" v-model="searchQuery" />
  <button @click="focusInput">Focus Input</button>
</template>

<script setup>
import { ref } from 'vue'
import MyInput from './MyInput.vue'

const searchQuery = ref('')
const myInputRef = ref(null)

function focusInput() {
  myInputRef.value.focus()  // Calling Methods Exposed by Child Components
  myInputRef.value.select() // You can also use chained calls
}
</script>

(3) 5 حالات استخدام رئيسية

السيناريو تم الكشف عنه بواسطة المكون الفرعي تم استدعاؤه بواسطة المكون الرئيسي
تسليط الضوء على النموذج focus() inputRef.focus()
مسح النموذج clear() formRef.clear()
إعادة تحميل البيانات reload() tableRef.reload()
فتح نافذة منبثقة open() modalRef.open()
إرسال النموذج submit() formRef.submit()


6. الarray ref في v-for

(1) الاستخدام الأساسي

VUE
<template>
  <ul>
    <!-- In v-for, ref auto-collected into array -->
    <li v-for="item in items" :key="item.id" ref="itemRefs">
      {{ item.name }}
    </li>
  </ul>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const items = ref([
  { id: 1, name: 'Apple' },
  { id: 2, name: 'Banana' },
  { id: 3, name: 'Cherry' }
])

// ✅ Array Format
const itemRefs = ref([])

onMounted(() => {
  // Page 2 Items DOM
  itemRefs.value[1].style.color = 'red'
})
</script>

(2) المرجع الديناميكي (v-for مع عدد ديناميكي)

VUE
<template>
  <button v-for="i in count" :key="i" :ref="el => buttonRefs[i] = el">
    Button {{ i }}
  </button>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const count = ref(3)
const buttonRefs = ref({})

onMounted(() => {
  // buttonRefs[0] = 1st button
  // buttonRefs[1] = 2nd button
  console.log(buttonRefs.value[0])
})
</script>

(3) 5 نقاط أساسية يجب أخذها في الاعتبار

نقاط يجب ملاحظتها الوصف
ترتيب الarray يتطابق مع ترتيب البيانات في v-for
تفاعلي تتطلب التغييرات التي تطرأ على الarray «ref» تعيين «watch»
العرض الشرطي قد لا يتم تحديث المراجع بعد v-if
عدد المتغيرات الديناميكية استخدام مرجع كائن أو مرجع دالة
الأداء قد يؤدي وجود عدد كبير من المراجع (100+) إلى إبطاء عملية العرض


7. أمثلة كاملة: 5 سيناريوهات واقعية

▶ مثال: 1. التركيز التلقائي على نموذج تسجيل الدخول

VUE
<template>
  <form @submit.prevent="handleLogin">
    <input ref="usernameRef" v-model="username" placeholder="Username">
    <input ref="passwordRef" v-model="password" type="password" placeholder="Password">
    <button>Login</button>
  </form>
</template>

<script setup>
import { ref, onMounted, useTemplateRef } from 'vue'

// ❌ Old notation
const usernameRef = ref(null)
const passwordRef = ref(null)

// ✅ New Writing Style(Vue 3.5+)
// const usernameRef = useTemplateRef('usernameRef')
// const passwordRef = useTemplateRef('passwordRef')

const username = ref('')
const password = ref('')

onMounted(() => {
  usernameRef.value.focus()  // Autofocus Username
})

function handleLogin() {
  console.log('Login:', username.value, password.value)
}
</script>
▶ جرّب الكود

▶ مثال: 2. التمرير التلقائي إلى أسفل الصفحة

VUE
<template>
  <div ref="messagesRef" class="messages">
    <div v-for="msg in messages" :key="msg.id">{{ msg.text }}</div>
  </div>
  <input v-model="newMessage" @keyup.enter="sendMessage">
  <button @click="sendMessage">Send</button>
</template>

<script setup>
import { ref, nextTick } from 'vue'

const messages = ref([])
const newMessage = ref('')
const messagesRef = ref(null)

async function sendMessage() {
  messages.value.push({ id: Date.now(), text: newMessage.value })
  newMessage.value = ''
  
  // ✅ Wait for DOM update, then scroll
  await nextTick()
  messagesRef.value.scrollTop = messagesRef.value.scrollHeight
}
</script>
▶ جرّب الكود

▶ مثال: 3. استدعاء المكون الأصلي لطريقة المكون التابع

VUE 📖 للعرض فقط
<!-- Child component:FormValidator.vue -->
<template>
  <form>
    <input v-model="email" placeholder="Email">
    <input v-model="password" type="password" placeholder="Password">
  </form>
</template>

<script setup>
import { ref } from 'vue'

const email = ref('')
const password = ref('')

const emailRef = ref(null)
const passwordRef = ref(null)

defineExpose({
  validate: () => {
    if (!email.value) {
      emailRef.value.focus()
      return false
    }
    if (!password.value || password.value.length < 6) {
      passwordRef.value.focus()
      return false
    }
    return true
  },
  reset: () => {
    email.value = ''
    password.value = ''
  }
})
</script>

<!-- Parent Component:LoginPage.vue -->
<template>
  <FormValidator ref="formRef" />
  <button @click="submit">Submit</button>
</template>

<script setup>
import { ref } from 'vue'
import FormValidator from './FormValidator.vue'

const formRef = ref(null)

function submit() {
  if (formRef.value.validate()) {
    console.log('Valid!')
  } else {
    console.log('Invalid!')
  }
}
</script>
46 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

▶ مثال: 4. استنتاج النوع لـ useTemplateRef

VUE
<template>
  <input ref="usernameInput" type="text">
  <MyChart ref="chartComponent" :data="chartData" />
</template>

<script setup lang="ts">
import { ref, useTemplateRef, onMounted } from 'vue'
import MyChart from './MyChart.vue'

// ✅ TypeScript Inference:HTMLInputElement | null
const usernameInput = useTemplateRef<HTMLInputElement>('usernameInput')

// ✅ TypeScript Inference:InstanceType<typeof MyChart> | null
const chartComponent = useTemplateRef<InstanceType<typeof MyChart>>('chartComponent')

onMounted(() => {
  // usernameInput.value Automatic is HTMLInputElement
  usernameInput.value?.focus()
  
  // chartComponent.value "Auto" is a component instance
  chartComponent.value?.refresh()
})
</script>
▶ جرّب الكود

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

الخطأ الأعراض الحل
خطأ إملائي في اسم القالب قيمة المرجع فارغة تأكد من تطابق أسماء المراجع
الوصول في المستوى الأعلى setup ref.value قيمة فارغة استخدم onMounted
الوصول داخل نافذة منبثقة لا يعمل استخدم nextTick أو طرق DOM مشابهة
الوصول إلى الarray المرجعية في v-for عدم توافق الفهرس استخدم :key للحفاظ على الترتيب
مرجع مشترك بين المكونات غير محدد المكونات الفرعية تستخدم defineExpose

▶ مثال: 6. مقارنة بين أنواع المراجع الخمسة الرئيسية

النوع مثال ينطبق على
عنصر DOM ref="inputRef" → HTMLInputElement الوصول إلى input/div
مثيل المكون ref="childRef" → مثيل المكون أساليب مكون النغمة
array v-for ref="itemRefs" → array الوصول إلى عناصر القائمة
مرجع وظيفي :ref="el => ..." → عنصر واحد العدد الديناميكي
مرجع سلسلة ref="name" → this.$refs نمط العرض 2 (غير موصى به)


❓ أسئلة شائعة

س متى ينبغي استخدام «Template Refs»؟
ج في أربع حالات: (1) التعامل مع DOM (التركيز/التمرير/اللوحة القماشية)؛ (2) استدعاء أساليب المكونات؛ (3) دمج مكتبات الجهات الخارجية (ECharts/Mapbox)؛ (4) قياس أبعاد العناصر. أما في الحالات الأخرى، فاستخدم «refs» أو «reactive» أو «props».
س هل useTemplateRef مطلوب في Vue 3.5+؟
ج نعم. فهو مدعوم فقط في Vue 3.5+. في Vue 3.4 والإصدارات الأقدم، استخدم المتغير ref. يُفضل استخدام استدلال الأنواع في TypeScript.
س ما الفرق بين «Template Refs» وuseRef (React)؟
ج تُرجع useRef في React مرجعًا قابلًا للتعديل، بينما يُرجع «template ref» في Vue كائن مرجع. يحتوي useTemplateRef في Vue 3.5+ على واجهة برمجة تطبيقات (API) أقرب إلى useRef في React.
س ما هي الطرق التي يجب أن يُتيحها المكون الفرعي؟
ج لا تُتيح سوى الطرق التي يحتاجها المكون الأصلي فعليًّا (مثل focus و clear و validate). لا تُتيح الطرق الداخلية الأخرى (مبدأ التغليف).
س متى يتم تحديث array المرجع في v-for؟
ج يتم تحديثها في كل مرة يتم فيها إعادة عرض v-for. كما يتم تحديثها عند تبديل status v-if. يمكنك استخدام watch على array المرجع للاستجابة للتغييرات.
س هل تتعارض مرجعيات القوالب مع وظائف provide/inject؟
ج لا، لا تتعارض. تُستخدم مرجعيات القوالب لـ«الوصول من العنصر الأصلي إلى العنصر الفرعي»، بينما تُستخدم وظائف provide/inject لـ«مشاركة البيانات عبر المستويات». ويمكن استخدامهما معًا.
س كيف يمكنني إضافة نوع إلى مرجع في TypeScript؟
ج استخدم useTemplateRef<HTMLInputElement>('usernameInput'). أو ref<HTMLInputElement | null>(null).

📖 ملخص


📝 تمارين

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

    تنفيذ نموذج بسيط للتركيز التلقائي:

    • حقل إدخال واحد + زر واحد
    • يتم تحديد حقل الإدخال تلقائيًا بعد تحميل الصفحة
    • عند النقر على الزر، يتغير نص الزر إلى «تم الإرسال» ويتم تعطيل الزر
  2. مسائل متقدمة (مستوى الصعوبة: ⭐⭐)

    تنفيذ مربع دردشة يتحرك تلقائيًا إلى أسفل الصفحة:

    • قائمة الرسائل (عنصر div الحاوي)
    • حقل الإدخال + زر «إرسال»
    • بعد إرسال الرسالة، تُضاف إلى القائمة ويتم تمريرها تلقائيًا إلى أسفل القائمة
    • استخدم v-for لعرض الرسائل، وarray ref لإدارة DOM
  3. مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)

    تنفيذ نظام متكامل لـ «المكونات الأبوية والفرعية + Template Refs»:

    1. المكون الفرعي FormValidator: يوفر طريقتي validate() و reset()
    2. المكون الأصلي LoginPage: يستدعي الأسلوب validate لإجراء عملية التحقق من الصحة؛ ويعرض رسالة خطأ في status فشل عملية التحقق من الصحة
    3. 5 حقول إدخال (اسم المستخدم/البريد الإلكتروني/كلمة المرور/رقم الهاتف/اختبار كابتشا)
    4. useTemplateRef (Vue 3.5+) + الكتابة القوية في TypeScript
    5. ضبط التركيز تلقائيًا على حقل الإدخال الأول عند فتح النافذة المنبثقة
    6. عند حدوث خطأ، ركز على حقل الخطأ الأول
Web-Tutorial.com

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

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

100%