Vue.js: المشاهدون: watch
آخر تحديث: 2026-08-26
تتيح لك المُستمعات (watch / watchEffect) مراقبة التغييرات في البيانات التفاعلية وتنفيذ الآثار الجانبية عند حدوث تلك التغييرات. تُستخدم computed لـ«استنباط قيم جديدة»، بينما تُستخدم watch لـ«تشغيل الإجراءات» (مثل إرسال الطلبات أو تعيين الstatus).
يُعد watch مكونًا أساسيًّا في نظام التفاعلية في Vue، وله ثلاث حالات استخدام أساسية: تشغيل استدعاءات واجهة برمجة التطبيقات (API) عند تغير البيانات، وإزالة الارتداد في إدخالات النماذج، والمزامنة بين البيانات.
1. ما ستتعلمه
watch()القواعد الأساسية و4 معلمات (المصدر / استدعاء الرد / الخيارات / العمق)- تعرف على 4 أنواع من مصادر البيانات: المرجعية، والتفاعلية، والمحسوبة، ووظائف الاسترجاع
watchEffect()الفرق بين التتبع التلقائي للتبعيات وwatchimmediate: trueخيار التشغيل الفوريdeep: trueالمراقبة المتعمقة للخصائص الداخلية- تطبيق آلية الحد من الاهتزاز
- معالجة الآثار الجانبية (onCleanup / إلغاء تحميل المكون)
2. مشكلات في أداء واجهة برمجة التطبيقات (API) المتعلقة بمربع البحث
(2) المشكلة: يتم إرسال طلب «requisition» مع كل ضغطة مفتاح، مما يتسبب في تعطل واجهة برمجة التطبيقات (API)
قامت أليس بإضافة مربع بحث عن المنتجات في لوحة الإدارة:
// ❌ The "Broken" Version:A request is sent for each character
import { ref } from 'vue'
const searchQuery = ref('')
watch(searchQuery, async (newValue) => {
// A request is sent with every input
const results = await fetch(`/api/search?q=${newValue}`)
searchResults.value = await results.json()
})
تعطل خادم واجهة برمجة التطبيقات (API):
- يكتب المستخدم «iPhone 15» (8 أحرف) = 8 استدعاءات لواجهة برمجة التطبيقات (API)
- 100 مستخدم يكتبون في وقت واحد = 800 استدعاء لواجهة برمجة التطبيقات (API)
- الحد الأقصى لعدد الطلبات على واجهة برمجة التطبيقات (API): 100/دقيقة → تعطل الخادم
يقول مطور البنية الخلفية تشارلي:
"أليس، عملية البحث التي تقومين بها تسبب ضغطًا كبيرًا على واجهة برمجة التطبيقات. نحتاج إلى تطبيق آلية "ديبونسينغ". انتظري 300 مللي ثانية بعد توقف المستخدم عن الكتابة قبل إرسال الطلب."
(2) عرض الساعة + حل تثبيت الصورة
import { ref, watch } from 'vue'
const searchQuery = ref('')
const searchResults = ref([])
// Image Stabilization:300ms A request is only sent if there are no internal changes.
let timeoutId = nulo
watch(searchQuery, (newValue) => {
if (timeoutId) clearTimeout(timeoutId)
timeoutId = setTimeout(async () => {
const response = await fetch(`/api/search?q=${newValue}`)
searchResults.value = await response.json()
}, 300)
})
(3) الإيرادات
بعد تطبيق آلية منع الارتداد:
- استدعاءات واجهة برمجة التطبيقات (API): 8 لكل عملية بحث → 1 لكل عملية بحث (-87%)
- حمل الخادم: 800 → 100 (-87%)
- زمن الاستجابة: 50 مللي ثانية (من ذاكرة التخزين المؤقت) مقابل 500 مللي ثانية (مباشر)
- وقت تشغيل الخادم: 99.9% (لا مزيد من حالات التعطل)
3. القواعد الأساسية لـ watch()
(1) 4 معلمات
import { ref, watch } from 'vue'
const count = ref(0)
watch(
// 1. Monitoring Source:Reactive data to be monitored
count,
// 2. Callback function:(New Value, Old value) => {...}
(newVal, oldVal) => {
console.log(`count: ${oldVal} → ${newVal}`)
},
// 3. Options: immediate, deep, flush
{ immediate: true, deep: true }
)
(2) استمع إلى الحكم
const count = ref(0)
// Monitoring ref
watch(count, (newVal, oldVal) => {
console.log(`count: ${oldVal} → ${newVal}`)
})
(3) مراقبة المراجع المتعددة
const firstName = ref('Alice')
const lastName = ref('Smith')
// Monitor multiple
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`Name: ${newFirst} ${newLast}`)
})
4. 4 أنواع من مصادر الاستماع
(1) استمع إلى الحكم
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`count: ${oldVal} → ${newVal}`)
})
(2) الاستماع إلى الكائنات التفاعلية
import { reactive, watch } from 'vue'
const user = reactive({ name: 'Alice', age: 25 })
// Monitor the entire reactive(Automatic Depth)
watch(user, (newVal, oldVal) => {
console.log('User changed:', newVal)
})
// ✅ Modifying internal properties also triggers
user.name = 'Bob' // Trigger
(3) الاستماع إلى دوال getter
const state = reactive({ user: { profile: { name: 'Alice' } } })
// Monitor only specific properties(Precise Control)
watch(
() => state.user.profile.name,
(newVal) => {
console.log(`Name: ${newVal}`)
}
)
(4) استمع إلى computed
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
watch(doubleCount, (newVal) => {
console.log(`Double: ${newVal}`)
})
(4) مقارنة بين 4 أنواع من مصادر الرصد
| مصدر البيانات | الترميز | تحليل متعمق | قابلية التطبيق |
|---|---|---|---|
| مرجع | watch(ref, fn) |
الإضاءة الافتراضية | مرجع واحد |
| تفاعلي | watch(reactive, fn) |
عميق تلقائي | كائن |
| getter | watch(() => obj.x, fn) |
افتراضي (سطحي) | خاصية محددة |
| محسوب | watch(computed, fn) |
الإضاءة الافتراضية | القيمة المشتقة |
5. watchEffect(): التتبع التلقائي للتبعيات
(1) قواعد النحو الأساسية
import { ref, watchEffect } from 'vue'
const count = ref(0)
const name = ref('Alice')
// You don't need to specify who to listen for.,Reactive data used within the function body is automatically tracked
watchEffect(() => {
console.log(`count: ${count.value}, name: ${name.value}`)
// count Re-execute when changes occur
// name Re-execute when changes occur
})
(2) watchEffect مقابل watch
| الجانب | watch | watchEffect |
|---|---|---|
| مصدر الصوت | محدد يدويًّا | التتبع التلقائي |
| تنفيذ الآن | الإعداد الافتراضي: لا (يتطلب immediate: true) |
✅ نعم |
| القيم القديمة والجديدة | ✅ كلاهما | ❌ القيمة الجديدة فقط |
| الآثار الجانبية لعملية التنظيف | onCleanup | onCleanup |
| حالات الاستخدام | المراقبة الدقيقة | الآثار الجانبية العامة |
(3) تنظيف watchEffect
import { watchEffect, onCleanup } from 'vue'
watchEffect((onCleanup) => {
// Set a Timer, Subscribe, Event Listening
const timer = setInterval(() => {
console.log('tick')
}, 1000)
// Cleanup:Called before the next execution or when the component is unloaded
onCleanup(() => {
clearInterval(timer)
})
})
6. الخيارات: فوري / عميق / متساوي
(1) فوري: يتم تشغيله على الفور
const count = ref(0)
// Default:count Change is what triggers it
watch(count, (newVal) => console.log(newVal)) // Will not be executed immediately
// immediate: true: Trigger Immediately 1 time
watch(count, (newVal) => console.log(newVal), { immediate: true })
// ✅ Execute Now:0 → 0
(2) deep: المراقبة العميقة
import { ref, watch } from 'vue'
const user = ref({ profile: { name: 'Alice' } })
// Default:ref The object being wrapped .value It is triggered only when the entire replacement is made
watch(user, () => console.log('changed')) // ❌ user.value.profile.name = 'Bob' Does not trigger
// deep: true:Changes to internal properties also trigger
watch(user, () => console.log('changed'), { deep: true })
// ✅ user.value.profile.name = 'Bob' Trigger
(3) flush: عند تشغيلها
// Default 'pre':Triggered before the component is updated
watch(source, fn, { flush: 'pre' })
// 'post':Triggered after a component update(DOM Updated)
watch(source, fn, { flush: 'post' })
// 'sync':Synchronous Triggering(Not commonly used,Poor performance)
watch(source, fn, { flush: 'sync' })
7. مثال كامل: مربع البحث + إزالة الارتداد
▶ مثال: 1. ساعة أساسية للحكم
<template>
<div>
<input v-model="count">
<p>Count: {{ count }}</p>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`Count changed: ${oldVal} → ${newVal}`)
})
</script>
▶ مثال: 2. منع الارتداد في البحث
<template>
<div>
<input v-model="searchQuery" placeholder="Search products...">
<p>Results: {{ results.length }} items</p>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const searchQuery = ref('')
const results = ref([])
let timeoutId = null
watch(searchQuery, (newQuery) => {
// Clear the previous timer
if (timeoutId) clearTimeout(timeoutId)
// Set a new timer:300ms Send the request afterward
timeoutId = setTimeout(async () => {
if (newQuery.length === 0) {
results.value = []
return
}
const res = await fetch(`/api/search?q=${newQuery}`)
results.value = await res.json()
}, 300)
})
</script>
▶ مثال: 3. الانتباه إلى التغيرات في عمق التفاعل
import { reactive, watch } from 'vue'
const user = reactive({
profile: { name: 'Alice', age: 25 },
settings: { theme: 'dark' }
})
// Monitor the entire user(Automatic Depth)
watch(user, (newVal) => {
console.log('User changed:', JSON.stringify(newVal))
})
// ✅ Modifying any property triggers
user.profile.name = 'Bob' // Trigger
user.settings.theme = 'light' // Trigger
▶ مثال: 4. التتبع التلقائي لـ watchEffect
import { ref, watchEffect } from 'vue'
const count = ref(0)
const name = ref('Alice')
// Do not specify a source to listen to,Automatic tracking used in the function body
watchEffect(() => {
console.log(`count: ${count.value}, name: ${name.value}`)
// count Re-execute when changes occur
// name Re-execute when changes occur
})
// Execute Now 1 time
count.value = 1 // Re-execute
name.value = 'Bob' // Re-execute
▶ مثال: 5. تطبيق تقنية منع الارتداد (Debounce) وتقنية التحكم في معدل الإرسال (Throttling)
import { ref, watch } from 'vue'
const searchQuery = ref('')
// Image Stabilization:300ms Last trigger inside
let debounceId = null
watch(searchQuery, (val) => {
if (debounceId) clearTimeout(debounceId)
debounceId = setTimeout(() => {
console.log('Search:', val)
}, 300)
})
// Cost-Cutting: Max 1 trigger per second
const scrollY = ref(0)
let lastRun = 0
watch(scrollY, (val) => {
const now = Date.now()
if (now - lastRun >= 1000) {
lastRun = now
console.log('Scroll:', val)
}
})
▶ مثال: 6. مرجع سريع لـ 5 أخطاء شائعة
| الخطأ | الأعراض | الحل |
|---|---|---|
| الاستماع إلى التفاعلات دون تعيين "deep" | التغييرات في الخصائص الداخلية لا تؤدي إلى تشغيل التفاعلات | watch(obj, fn, { deep: true }) |
| الاستماع إلى كائن غلاف المرجع بالكامل | التغييرات في الخصائص الداخلية لا تؤدي إلى تشغيل | التبديل إلى الوضع التفاعلي أو إضافة "deep" |
| تُنشئ watchEffect حلقة لا نهائية | يتجمد المتصفح | تجنب تعديل البيانات المستخدمة داخل الدالة |
| لم يتم تنظيف المؤقت | تسرب في الذاكرة | استخدم onCleanup |
| تنفيذ عملية التعتيم على الفور | أريد استخدام القيمة القديمة ولكن لم أحصل عليها | استخدام immediate + oldVal (قد تكون غير معرفة) |
❓ أسئلة شائعة
watch أم watchEffect؟watch كلما أمكن ذلك (للتحكم الدقيق والوصول إلى القيم القديمة والجديدة). watchEffect مناسب للحالات التي تريد فيها تنفيذ إجراء عند تغير بيانات معينة، ولكنك لا تريد سرد جميع التبعيات. من ناحية الأداء، فهي متشابهة تقريبًا، على الرغم من أن watchEffect أبطأ قليلاً (لأنه يحتاج إلى تتبع التبعيات داخل جسم الدالة).ref، ما هي أنواع القيم القديمة والجديدة؟.value. إذا كان ref يغلف كائنًا، فإن القيمتين القديمة والجديدة تكونان ref.value (تكون المراجع هي نفسها، ما لم يتم استبدال القيمة بالكامل). لمراقبة التغييرات التي تطرأ على الخصائص الداخلية، استخدم reactive أو دالة الحصول على القيمة.watch لمراقبة كائن غلاف ref، كيف يمكنك مراقبة خصائصه الداخلية؟watch(ref, fn, { deep: true })؛ (2) watch(() => ref.value.x, fn) (دالة الحصول)؛ (3) التبديل إلى غلاف تفاعلي.watch مراقبة المتغيرات؟watch(() => props.count, fn). لاحظ أن المتغيرات هي للقراءة فقط؛ فلا تقم بتعديلها في دالة الاستدعاء watch.oldVal عندما يكون immediate: true؟undefined (لا توجد «قيمة سابقة» عند التشغيل الأول). يمكنك استرداد القيمة السابقة من القيمة الأولية للمُرجع أو من المخزن.watch غير متزامنة؟async غير متزامنة، لكن watch نفسها يتم تشغيلها بشكل متزامن. ضع المنطق غير المتزامن داخل دالة الاستدعاء: (newVal) => { setTimeout(() => {...}, 0) }.onCleanup؟📖 ملخص
- watch: يراقب التغييرات في البيانات التفاعلية؛ 4 معلمات: المصدر / الاستدعاء المرتد / الخيارات / العمق
- 4 أنواع من المراقبين: ref / reactive / دالات getter / computed
- تقوم watchEffect تلقائيًا بتتبع التبعيات التفاعلية داخل نص الدالة وتنفيذها مرة واحدة على الفور
- immediate: true — يتم تشغيله على الفور؛ deep: true — يراقب الخصائص الداخلية بشكل متعمق
- منع التكرار: لا يتم تنفيذه إلا إذا حدث آخر تشغيل خلال 300 مللي ثانية؛ التحكم في السرعة: لا يزيد عن مرة واحدة في الثانية
- onCleanup: تنظيف الآثار الجانبية: المؤقتات، والاشتراكات، ومستمعو الأحداث
- watch مقابل watchEffect: توفر watch تحكماً دقيقاً وتتابع القيم القديمة والجديدة على حد سواء؛ بينما تتبع watchEffect التغييرات تلقائيًا وتتميز بكونها أبسط
📝 تمارين
-
أسئلة أساسية (مستوى الصعوبة: ⭐)
استخدم
watchلمراقبة المتغيرrefcount:- عند تغير القيمة، اطبع "القيمة: ${old} → ${new}"
- استخدم
immediate: trueلتنفيذ عملية الاستدعاء الفوري مرة واحدة
-
مسائل متقدمة (مستوى الصعوبة: ⭐⭐)
تنفيذ ميزة «الحفظ التلقائي» باستخدام watch:
- تعديل نص مرجع
content - يقوم تلقائيًا باستدعاء الدالة
save(content)بعد مرور ثانية واحدة (إزالة الارتداد) - مسح المؤقت عند الانتقال إلى صفحة أخرى (إغلاق المكون)
- تعديل نص مرجع
-
مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)
تنفيذ نظام شامل للبحث والإكمال التلقائي وسجل التصفح:
- البحث المضاد للاهتزاز (300 مللي ثانية)
- عرض قائمة نتائج البحث (v-for)
- حفظ آخر 5 عمليات بحث
- انقر على عنصر من السجل لإجراء البحث مرة أخرى
- إلغاء الطلبات المعلقة عند تغيير مصطلحات البحث
- التنفيذ باستخدام
watchEffectوonCleanup