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. ما ستتعلمه
ref="el"أساسيات الإشارات إلى القوالبuseTemplateRef(موصى به لـ Vue 3.5+)- الوصول إلى عناصر DOM (التركيز، والتمرير إلى منطقة العرض، وما إلى ذلك)
- الوصول إلى مثيل مكون فرعي (باستخدام الطريقة
defineExpose) $refsصيغة بديلة باستخدام واجهة برمجة التطبيقات (API) للتركيب- مصفوفات المرجع في
v-for - 5 حالات استخدام رئيسية و4 أنماط غير مرغوب فيها
2. معضلة «التركيز التلقائي» في نموذج تسجيل الدخول
(1) المشكلة: كيف يمكن أن يحصل حقل الإدخال على التركيز تلقائيًّا عند فتح النافذة المنبثقة؟
قامت أليس بإنشاء نافذة منبثقة لتسجيل الدخول من المفترض أن تركز تلقائيًا على حقل إدخال اسم المستخدم:
// ❌ 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) عرض حل «مراجع القوالب»
<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>
<!-- 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»:
- سهولة قراءة الكود: اذكر بوضوح «أريد الإشارة إلى هذا العنصر»
- يتوافق مع فلسفة Vue: لا يتعامل مباشرةً مع DOM
- أمان الأنواع: يستنتج TypeScript أنواع العناصر
- دورة الحياة الصحيحة: الوصول في
onMountedأوnextTick
3. الاستخدام الأساسي لـ ref
(1) مرجع السلسلة (بأسلوب Vue 2، تم إهماله)
// ❌ Not recommended:String ref
export default {
mounted() {
this.$refs.input.focus()
}
}
(2) المتغير ref (موصى به في Vue 3)
<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 عمليات أساسية
// 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 أكثر قوة.
<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) مقارنة شاملة
<!-- 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
<!-- 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) الوصول إلى المكون الأصلي
<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) الاستخدام الأساسي
<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 مع عدد ديناميكي)
<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. التركيز التلقائي على نموذج تسجيل الدخول
<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. التمرير التلقائي إلى أسفل الصفحة
<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. استدعاء المكون الأصلي لطريقة المكون التابع
<!-- 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>
▶ مثال: 4. استنتاج النوع لـ useTemplateRef
<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 (غير موصى به) |
❓ أسئلة شائعة
useTemplateRef مطلوب في Vue 3.5+؟ref. يُفضل استخدام استدلال الأنواع في TypeScript.useRef (React)؟useRef في React مرجعًا قابلًا للتعديل، بينما يُرجع «template ref» في Vue كائن مرجع. يحتوي useTemplateRef في Vue 3.5+ على واجهة برمجة تطبيقات (API) أقرب إلى useRef في React.v-for؟v-for. كما يتم تحديثها عند تبديل status v-if. يمكنك استخدام watch على array المرجع للاستجابة للتغييرات.useTemplateRef<HTMLInputElement>('usernameInput'). أو ref<HTMLInputElement | null>(null).📖 ملخص
- تُستخدم «مراجع القوالب» للوصول مباشرةً إلى عناصر DOM أو مثيلات المكونات الفرعية
- 3 طرق لكتابتها: مرجع متغير (Vue 3) / useTemplateRef (Vue 3.5+؛ موصى به) / مرجع سلسلة (نمط Vue 2؛ غير موصى به)
- 5 عمليات أساسية: التركيز / التمرير / التحديد / تغيير الخصائص / ضبط المكونات
defineExposeيسمح للمكون الفرعي بإتاحة طرق للمكون الأصلي- في
v-for، يتم تجميعrefتلقائيًا في array - 5 سيناريوهات رئيسية: التركيز التلقائي / التمرير إلى أسفل الصفحة / طريقة تحديد الدرجة اللونية / التكامل مع تطبيقات خارجية / قياس حجم العنصر
- 4 أنماط خاطئة: الأخطاء الإملائية / الوصول إلى المستوى الأعلى / النوافذ المنبثقة التي لا تستخدم
nextTick/ التداخل بين المكوناتref
📝 تمارين
-
أسئلة أساسية (مستوى الصعوبة: ⭐)
تنفيذ نموذج بسيط للتركيز التلقائي:
- حقل إدخال واحد + زر واحد
- يتم تحديد حقل الإدخال تلقائيًا بعد تحميل الصفحة
- عند النقر على الزر، يتغير نص الزر إلى «تم الإرسال» ويتم تعطيل الزر
-
مسائل متقدمة (مستوى الصعوبة: ⭐⭐)
تنفيذ مربع دردشة يتحرك تلقائيًا إلى أسفل الصفحة:
- قائمة الرسائل (عنصر div الحاوي)
- حقل الإدخال + زر «إرسال»
- بعد إرسال الرسالة، تُضاف إلى القائمة ويتم تمريرها تلقائيًا إلى أسفل القائمة
- استخدم
v-forلعرض الرسائل، وarrayrefلإدارة DOM
-
مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)
تنفيذ نظام متكامل لـ «المكونات الأبوية والفرعية + Template Refs»:
- المكون الفرعي FormValidator: يوفر طريقتي validate() و reset()
- المكون الأصلي LoginPage: يستدعي الأسلوب
validateلإجراء عملية التحقق من الصحة؛ ويعرض رسالة خطأ في status فشل عملية التحقق من الصحة - 5 حقول إدخال (اسم المستخدم/البريد الإلكتروني/كلمة المرور/رقم الهاتف/اختبار كابتشا)
- useTemplateRef (Vue 3.5+) + الكتابة القوية في TypeScript
- ضبط التركيز تلقائيًا على حقل الإدخال الأول عند فتح النافذة المنبثقة
- عند حدوث خطأ، ركز على حقل الخطأ الأول