Vue.js: المشاهدون: watch

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

تتيح لك المُستمعات (watch / watchEffect) مراقبة التغييرات في البيانات التفاعلية وتنفيذ الآثار الجانبية عند حدوث تلك التغييرات. تُستخدم computed لـ«استنباط قيم جديدة»، بينما تُستخدم watch لـ«تشغيل الإجراءات» (مثل إرسال الطلبات أو تعيين الstatus).

يُعد watch مكونًا أساسيًّا في نظام التفاعلية في Vue، وله ثلاث حالات استخدام أساسية: تشغيل استدعاءات واجهة برمجة التطبيقات (API) عند تغير البيانات، وإزالة الارتداد في إدخالات النماذج، والمزامنة بين البيانات.

1. ما ستتعلمه



2. مشكلات في أداء واجهة برمجة التطبيقات (API) المتعلقة بمربع البحث

(2) المشكلة: يتم إرسال طلب «requisition» مع كل ضغطة مفتاح، مما يتسبب في تعطل واجهة برمجة التطبيقات (API)

قامت أليس بإضافة مربع بحث عن المنتجات في لوحة الإدارة:

JS
// ❌ 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):

يقول مطور البنية الخلفية تشارلي:

"أليس، عملية البحث التي تقومين بها تسبب ضغطًا كبيرًا على واجهة برمجة التطبيقات. نحتاج إلى تطبيق آلية "ديبونسينغ". انتظري 300 مللي ثانية بعد توقف المستخدم عن الكتابة قبل إرسال الطلب."

(2) عرض الساعة + حل تثبيت الصورة

JS
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) الإيرادات

بعد تطبيق آلية منع الارتداد:



3. القواعد الأساسية لـ watch()

(1) 4 معلمات

JS
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) استمع إلى الحكم

JS
const count = ref(0)

// Monitoring ref
watch(count, (newVal, oldVal) => {
  console.log(`count: ${oldVal} → ${newVal}`)
})

(3) مراقبة المراجع المتعددة

JS
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) استمع إلى الحكم

JS
const count = ref(0)
watch(count, (newVal, oldVal) => {
  console.log(`count: ${oldVal} → ${newVal}`)
})

(2) الاستماع إلى الكائنات التفاعلية

JS
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

JS
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

JS
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) قواعد النحو الأساسية

JS
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

JS
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) فوري: يتم تشغيله على الفور

JS
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: المراقبة العميقة

JS
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: عند تشغيلها

JS
// 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. ساعة أساسية للحكم

VUE
<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. منع الارتداد في البحث

VUE
<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. الانتباه إلى التغيرات في عمق التفاعل

JS
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

JS
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)

JS
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، كيف يمكنك مراقبة خصائصه الداخلية؟
ج هناك ثلاث طرق: (1) 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؟
ج يتم تشغيله في حالتين: (1) قبل إعادة تنفيذ عملية المراقبة (بعد عملية التنظيف الأخيرة)؛ (2) عند إلغاء تثبيت المكون. وهو مناسب لتنظيف المؤقتات، وإلغاء الطلبات، وإزالة مستمعي الأحداث.

📖 ملخص


📝 تمارين

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

    استخدم watch لمراقبة المتغير ref count:

    • عند تغير القيمة، اطبع "القيمة: ${old} → ${new}"
    • استخدم immediate: true لتنفيذ عملية الاستدعاء الفوري مرة واحدة
  2. مسائل متقدمة (مستوى الصعوبة: ⭐⭐)

    تنفيذ ميزة «الحفظ التلقائي» باستخدام watch:

    • تعديل نص مرجع content
    • يقوم تلقائيًا باستدعاء الدالة save(content) بعد مرور ثانية واحدة (إزالة الارتداد)
    • مسح المؤقت عند الانتقال إلى صفحة أخرى (إغلاق المكون)
  3. مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)

    تنفيذ نظام شامل للبحث والإكمال التلقائي وسجل التصفح:

    1. البحث المضاد للاهتزاز (300 مللي ثانية)
    2. عرض قائمة نتائج البحث (v-for)
    3. حفظ آخر 5 عمليات بحث
    4. انقر على عنصر من السجل لإجراء البحث مرة أخرى
    5. إلغاء الطلبات المعلقة عند تغيير مصطلحات البحث
    6. التنفيذ باستخدام watchEffect وonCleanup
Web-Tutorial.com

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

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

100%