Vue.js: البيانات التفاعلية: ref/reactive

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

يُعد نظام التفاعلية في Vue 3 قلبها وروحها — فعندما تقوم بتعديل متغير ما، يتم تحديث كل مكان في الصفحة يستخدم فيه هذا المتغير تلقائيًا. ولهذا السبب، فإن كتابة Vue أسهل بعشر مرات من كتابة jQuery. ويعتمد نظام التفاعلية على واجهتي برمجة تطبيقات أساسيتين: ref() وreactive().

يُستخدم ref لتغليف أي نوع من البيانات (الأرقام، السلاسل، الكائنات)، بينما لا يمكن لـ reactive سوى تغليف الكائنات. ويختلف استخدام كل منهما عن الآخر، وسيساعدك هذا الدرس على فهم الفرق تمامًا.

1. ما ستتعلمه



2. قصة صفحة عداد سارت الأمور فيها على نحو خاطئ

(1) المشكلة: عندما يتغير العدد، لا يتم تحديث واجهة المستخدم

كتبت أليس أول مكون لها باستخدام Vue 3 من أجل لوحة إدارة موقع التجارة الإلكترونية. وحاولت تنفيذ «عداد مخزون المنتجات»:

JS
// ❌ The "Broken" Version
let count = 0

function increment() {
  count++
  console.log(count)  // 1, 2, 3... It looks like
}
HTML
<template>
  <p>Stock: {{ count }}</p>
  <button @click="increment">+1</button>
</template>

تضغط على الزر 5 مرات. تظهر على شاشة وحدة التحكم الأرقام 1، 2، 3، 4، 5. لكن الصفحة لا تزال تعرض "المخزون: 0".

تنظر مديرة المنتج تشارلي من فوق كتفها:

"أليس، لماذا لا يتم تحديث العدد؟ لقد نقرت 5 مرات. هل تعطل برنامج Vue؟"

(2) حلول Vue التفاعلية: ref() / reactive()

VUE
<!-- ✅ Correct Version -->
<template>
  <p>Stock: {{ count }}</p>
  <button @click="increment">+1</button>
</template>

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

// Use ref() Wrapper → Vue Knows it should be \"Reactive\"
const count = ref(0)

function increment() {
  // In JS Key Points .value
  count.value++
  // Automatic unwrapping in templates,Not necessary .value
}
</script>

الآن، عند النقر على الزر، يتم تحديث الصفحة على الفور. عندما يتغير المتغير → يتم تحديث DOM تلقائيًا.

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

بعد تعلم مبدأ التفاعلية في Vue 3:



3. مبادئ التفاعلية: الوكيل + تجميع التبعيات

(1) عملية الاستجابة المكونة من 3 خطوات

100%
sequenceDiagram
    participant T as Template
    participant C as Component
    participant R as Reactive<br/>(Proxy)
    participant D as Data
    
    T->>C: 1. Render (Read count)
    C->>R: 2. get count
    R->>D: 3. Read 0
    R->>R: 4. Track: Who's using it? count?
    R-->>C: 5. Return 0
    C-->>T: 6. Display "Stock: 0"
    
    Note over D,T: The user clicks the button
    
    D->>R: 7. set count = 1
    R->>R: 8. Trigger: Notice to All Users count the place
    R->>C: 9. Re-render
    C-->>T: 10. Display "Stock: 1"

(2) النقاط الرئيسية

(3) Vue 2 مقابل Vue 3: مقارنة بين التفاعلية

البعد المنظر 2 المنظر 3
واجهة برمجة التطبيقات الأساسية Object.defineProperty ES6 Proxy
مراقبو المصفوفات يتطلب معالجة خاصة دعم أصلي
الخريطة/ضبط الساعة غير مدعوم مدعوم
إضافة عقار جديد يتطلب Vue.set() الرد التلقائي
الأداء المتوسط أفضل (الوكيل البطيء)


4. ref(): يلف أي قيمة

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

JS
import { ref } from 'vue'

// Packaging Numbers
const count = ref(0)

// Packaging String
const name = ref('Alice')

// Boolean Packaging
const isVip = ref(false)

// Packaging nulo
const data = ref(nulo)

// Packaging Array
const item = ref([1, 2, 3])

// Items to Be Packaged
const user = ref({ name: 'Bob', age: 25 })

(2) الوصول إلى القيم: استخدم .value في JavaScript؛ حيث يقوم القالب بفك ضغطها تلقائيًا

VUE
<template>
  <!-- Using variable names directly in templates(Automatic Unpacking) -->
  <p>{{ count }}</p>          <!-- 0 -->
  <p>{{ name }}</p>          <!-- Alice -->
  <p>{{ user.name }}</p>     <!-- Bob -->
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
const name = ref('Alice')
const user = ref({ name: 'Bob', age: 25 })

function logCount() {
  // In JS Must use .value
  console.log(count.value)  // 0
  console.log(name.value)   // Alice
  console.log(user.value.name)  // Bob
}
</script>

(3) الطبيعة «ثنائية الاتجاه» لـ ref

JS
import { ref } from 'vue'

const count = ref(0)

// Read
console.log(count.value)  // 0

// Edit
count.value = 1
console.log(count.value)  // 1

// Modify Nested Properties(ref When wrapping an object)
const user = ref({ name: 'Bob' })
user.value.name = 'Alice'  // ✅ Responsive
user.value = { name: 'Charlie' }  // ✅ The entire replacement also responds


5. reactive(): كائن غلاف (التفاعلية العميقة)

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

JS
import { reactive } from 'vue'

// Only objects can be passed
const state = reactive({
  count: 0,
  user: { name: 'Alice', age: 25 },
  item: [1, 2, 3]
})

// No access required .value
console.log(state.count)  // 0
console.log(state.user.name)  // Alice

// Edit Auto-Reply
state.count = 1
state.user.name = 'Bob'
state.item.push(4)

(2) رد مفصل

JS
const state = reactive({
  level1: {
    level2: {
      level3: {
        value: 'deep'
      }
    }
  }
})

// Changes made at any level are automatically reflected
state.level1.level2.level3.value = 'updated'  // ✅ Trigger an update

(3) حدود البرمجة التفاعلية

JS
// ❌ You cannot directly replace the entire object.(No longer responsive)
let state = reactive({ count: 0 })
state = reactive({ count: 1 })  // Wrong! state variable itself changed, but template uses old reference.

// ❌ Deconstruction results in the loss of responsiveness
const state = reactive({ count: 0, name: 'Alice' })
const { count, name } = state  // count/name Convert to a regular variable,Not responding

// ✅ Destructuring Reactivity: use toRefs
import { toRefs } from 'vue'
const { count, name } = toRefs(state)  // count/name.value Stay Responsive


6. الفروق الخمس الرئيسية بين ref و reactive

الجانب المرجع التفاعلية
الأنواع القابلة للتغليف أي قيمة (عدد، سلسلة، كائن، array) الكائنات فقط (كائن/array)
طريقة الوصول .value (JS) الوصول المباشر (JS)
الوصول إلى القوالب الفك التلقائي (بدون .value) الوصول المباشر
استبدال الكل ref.value = {...} ✅ يستجيب reactive({...}) = ... ❌ لا يستجيب
التفكيك يتم الاحتفاظ بالردود (toRefs) تُفقد الردود (toRefs مطلوب)

(4) توصيات الاختيار

السيناريو التوصية
الأنواع الأولية (عدد/سلسلة/منطقية) ref()
كائن واحد متجاوب يُقبل إما ref({}) أو reactive({})
نظام يتألف من عدة مجالات مترابطة reactive({}) (مثل Vuex)
يجب استبداله بالكامل ref({}) (أكثر مرونة)
تمرير الدعائم / الإرسال باستخدام ref() (حدود واضحة)


7. التفاعلية السطحية: shallowRef / shallowReactive

(1) متى ينبغي استخدام الردود السطحية؟

عندما يكون لديك كائن كبير، ولكنك مهتم فقط بالتغييرات التي تطرأ على الحقول ذات المستوى الأعلى، فإن الاستجابة السطحية يمكن أن تحسّن الأداء.

JS
import { shallowRef, shallowReactive, triggerRef } from 'vue'

// shallowRef:Follow Only .value Replace All,Do not track internal properties
const bigData = shallowRef({ item: [...50000 item] })

bigData.value.item.push('new')  // ❌ Do not trigger an update
bigData.value = { item: [newItems] }  // ✅ Trigger an update(Replace All)

// shallowReactive:Track only top-level properties,No in-depth response
const state = shallowReactive({
  user: { name: 'Alice' },
  settings: { theme: 'dark' }
})

state.user.name = 'Bob'  // ❌ Does not trigger
state.user = { name: 'Bob' }  // ✅ Trigger(Top-Level Property Replacement)

(2) فرض التحديث

JS
import { triggerRef } from 'vue'

const bigData = shallowRef({ items: [...] })
bigData.value.items.push('new')  // Do not respond by default
triggerRef(bigData)  // Manually Trigger an Update

السيناريوهات النموذجية: الجداول الكبيرة (100,000 صف)، والقوائم الكبيرة، والحالات التي يتم فيها تعديل الخصائص الداخلية بشكل متكرر، لكن الstatus الإجمالية هي المهمة فقط.



8. toRef / toRefs: التفكيك مع الحفاظ على التفاعلية

(1) toRefs: تفكيك كائن تفاعلي

JS
import { reactive, toRefs } from 'vue'

const state = reactive({
  count: 0,
  name: 'Alice'
})

// ❌ General Deconstruction:Missing Response
const { count, name } = state

// ✅ toRefs Deconstruction:Stay Responsive
const { count, name } = toRefs(state)

// Used in templates
// <p>{{ count }}</p>  <!-- Still responding -->

(2) toRef: تحويل قيمة عادية إلى مرجع

JS
import { toRef, ref } from 'vue'

// Scene: Select one field from props
const props = defineProps({ count: Number })

// ❌ Directly Deconstructing Missing Responses
const { count } = props  // count It's ordinary numbervalue

// ✅ toRef Packaging
const count = toRef(props, 'count')  // count is ref(props.count)

// Usage
count.value++  // Responsive Changes props


9. 5 عقبات في التصميم المتجاوب

(1) المأزق الأول: الاستبدال المباشر لكائن تفاعلي

JS
let state = reactive({ count: 0 })
// ❌ state The entire variable has been changed,But the template uses the old version state
state = reactive({ count: 1 })
// ✅ Edit Properties
state.count = 1

(2) المأزق الثاني: تفكيك رد الفعل وفقدان الاستجابة

JS
const state = reactive({ count: 0 })
// ❌ count It is a regular variable
const { count } = state
// ✅ Use toRefs
const { count } = toRefs(state)

(3) المأزق الثالث: التعيين المباشر إلى المصفوفات

JS
const item = ref([1, 2, 3])
// ❌ shallowRef يتتبع فقط استبدال .value، وليس التغييرات الداخلية
items.value.push(4)  // لا يُطلق إعادة العرض
// ✅ استبدل المصفوفة بأكملها
items.value = [...items.value, 4]

// ملاحظة: مع ref() العادي، push تفاعلي (ref يلف المصفوفة في reactive proxy)
const list = ref([1, 2, 3])
list.value.push(4)  // ✅ يُطلق إعادة العرض في Vue 3

(4) المأزق الرابع: التعديل المباشر لمحتويات كائن محاط بـ ref

JS
// It generally does not expire.,However, there are edge cases
const user = ref({ name: 'Alice' })
user.value.name = 'Bob'  // ✅ Response(ref Wrapped objects are automatically unwrapped)

(5) المأزق الخامس: الخلط بين النوعين ref وreactive

JS
// ❌ Type error:reactive Cannot wrap the original value
const count = reactive(0)  // Error: value cannot be made reactive
// ✅ Use ref
const count = ref(0)


10. مثال كامل: عربة التسوق المتوافقة مع جميع الأحجام

▶ مثال: 1. الاستخدام الأساسي لـ ref

VUE
<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">+1</button>
    <button @click="reset">Reset</button>
  </div>
</template>

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

const count = ref(0)

function increment() {
  count.value++  // JS Must be used in .value
}

function reset() {
  count.value = 0
}
</script>
▶ جرّب الكود

▶ مثال: 2. كائنات التغليف التفاعلية

VUE
<template>
  <div>
    <p>User: {{ state.user.name }}</p>
    <p>Age: {{ state.user.age }}</p>
    <button @click="birthday">+1 Year</button>
  </div>
</template>

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

const state = reactive({
  user: { name: 'Alice', age: 25 }
})

function birthday() {
  state.user.age++  // Edit directly,Automatic Response
}
</script>
▶ جرّب الكود

▶ مثال: 3. مقارنة بين ref و reactive

JS
import { ref, reactive } from 'vue'

// ref Style
const userRef = ref({ name: 'Alice', age: 25 })
// Edit:
userRef.value.name = 'Bob'  // ✅ Response
userRef.value = { name: 'Charlie', age: 30 }  // ✅ Response

// reactive Style
const userReactive = reactive({ name: 'Alice', age: 25 })
// Edit:
userReactive.name = 'Bob'  // ✅ Response
// userReactive = reactive({...})  // ❌ Failure
▶ جرّب الكود

▶ مثال: 4. toRefs — تفكيك المتغيرات مع الحفاظ على الاستجابة

VUE
<template>
  <div>
    <p>{{ count }} - {{ name }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script setup>
import { reactive, toRefs } from 'vue'

const state = reactive({ count: 0, name: 'Alice' })
const { count, name } = toRefs(state)  // Stay Responsive

function increment() {
  count.value++  // count is ref, So use .value
}
</script>
▶ جرّب الكود

▶ مثال: 5. استجابة سطحية + triggerRef

JS
import { shallowRef, triggerRef } from 'vue'

// Large Table 100,000 rows → use shallowRef
const bigTable = shallowRef({
  rows: Array.from({ length: 100000 }, (_, i) => ({ id: i, name: `Row ${i}` }))
})

// Replace All(Highly efficient)
function reloadData(newRows) {
  bigTable.value = { rows: newRows }
}

// Modify Internal Properties(Not responding,Must be done manually trigger)
function updateRow(id, newName) {
  const row = bigTable.value.rows.find(r => r.id === id)
  if (row) {
    row.name = newName
    triggerRef(bigTable)  // Manually Trigger an Update
  }
}
▶ جرّب الكود

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

الخطأ الأعراض الحل
let state = reactive({...}); state = {...} استبدال الstatus بالكامل؛ لم يتم تحديث القالب تعديل الخاصية state.x = 1
const { count } = state تفكيك تحويل count إلى متغير عادي استخدام toRefs(state)
reactive(0) قيمة العبوة الأصلية خطأ استخدم ref() للقيمة الأصلية
count.value++ في القالب خطأ القالب مباشرةً {{ count }}
array[index] = x مع shallowRef لا يُطلق التحديث (shallow يتتبع .value فقط) استخدم splice أو استبدل المصفوفة بأكملها


❓ أسئلة شائعة

س أيهما يجب أن أختار: ref أم reactive؟
ج التوصية الرسمية هي استخدام ref حصريًّا (السبب: ref يعمل مع أي نوع ويوفر استبدالًا وتمريرًا أكثر وضوحًا للخصائص/الدوال بشكل عام). reactive مناسب للحالات التي «تتكون فيها الstatus من عدة حقول مرتبطة» (مثل Vuex). يُنصح المبتدئون بتعلم ref أولاً واستخدام reactive بمجرد أن يصبحوا أكثر إتقانًا.
س لماذا تحتاج المرجع (ref) إلى .value بينما لا يحتاج القالب إلى ذلك؟
ج تُستخدم .value لإخبار محرك جافا سكريبت صراحةً بأن «هذه قيمة مغلفة بمرجع؛ ويجب جعلها تفاعلية». تقوم Vue بتحليل القوالب بنفسها؛ فهي تعرف أي العناصر تمثل مراجع وتقوم بإزالة التغليف عنها تلقائيًا. باختصار: يُعد .value مطلوبًا في JavaScript (لتجنب الغموض)، بينما تقوم القوالب بإزالة تغليف المراجع تلقائيًا (للسهولة).
س عند استخدام ref لتغليف كائن، هل أحتاج إلى استخدام .value.x للوصول إلى خصائصه؟
ج لا. بعد ref({name: 'Alice'})، يستخدم القالب {{ user.name }} مباشرةً، بينما تستخدم JavaScript user.value.name. تتولى Vue تلقائيًا عملية الوصول إلى الخصائص الداخلية للكائن المغلف بواسطة ref.
س هل تؤدي عملية تفكيك المتغيرات التفاعلية إلى فقدان التفاعلية؟ كيف يمكن حل هذه المشكلة؟
ج قم بتضمينها بـ toRefs(). بعد const { count, name } = toRefs(state)، تصبح count وname مراجع؛ بينما تظل كل من {{ count }} في القالب وcount.value في جافا سكريبت تفاعليتين.
س ما الفرق بين shallowRef و ref؟
ج ref هي مرجع عميق (تؤدي التغييرات في الخصائص الداخلية إلى إحداث تغييرات أيضًا)، بينما shallowRef تتتبع فقط استبدال .value ككل (ولا تتتبع التغييرات الداخلية). عندما تكون مجموعة البيانات كبيرة (أكثر من 100,000 سجل) ولا يهمك سوى الاستبدال الكلي، فإن استخدام shallowRef يوفر أداءً أفضل.
س ما هي الأسباب الشائعة لفشل السلوك التفاعلي؟
ج الأسباب الخمسة الأكثر شيوعًا هي: (1) استبدال كائن تفاعلي مباشرةً؛ (2) تفكيك كائن تفاعلي دون استخدام toRefs؛ (3) تغليف قيمة بدائية في ref غير تفاعلي (استخدم reactive بدلاً من ذلك أو احتفظ بـ ref)؛ (4) إنشاء ref خارج setup (مما يؤدي إلى فقدان التفاعلية)؛ (5) التعيين المباشر إلى array (استخدم splice بدلاً من ذلك).
س هل يمكن لـ reactive أن يلف Map وSet؟
ج نعم. يدعم reactive في Vue 3، الذي تم تنفيذه باستخدام Proxy، Map وSet وWeakMap وWeakSet. لا يدعم Vue 2 ذلك.

📖 ملخص


📝 تمارين

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

    اكتب عدادًا بسيطًا لعدد العناصر في سلة التسوق باستخدام ref:

    • القيمة الأولية 1
    • زر "+": إضافة 1
    • يؤدي الضغط على الزر «-» إلى خفض القيمة بمقدار 1
    • تعطيل زر "-" عندما تكون الكمية ≤ 0 (:تعطيل ديناميكي)
    • تعطيل زر "+" عندما تكون الكمية 99 أو أكثر
  2. مسائل متقدمة (مستوى الصعوبة: ⭐⭐)

    اكتب نموذجًا لتعديل ملف تعريف المستخدم باستخدام Reactive:

    • يحتوي حقل «الstatus» على ثلاثة حقول: name وemail وage
    • الربط التفاعلي لثلاثة حقول إدخال (باستخدام حدث @input؛ وسنتناول v-model في الدرس التالي)
    • يعيد زر «إعادة الضبط» القيم الافتراضية
    • يقوم زر «إرسال» بطباعة الstatus الكاملة على وحدة التحكم
  3. مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)

    تنفيذ تطبيق مهام متكامل (طبقة البيانات):

    1. قم بتغليف الarray todos بـ reactive
    2. كل مهمة تتضمن { id, text, done }
    3. الطرق المتوفرة: addTodo(text) / toggleTodo(id) / removeTodo(id) / clearDone()
    4. توفير الخصائص المحسوبة التالية: activeCount (عدد العناصر غير المكتملة) و doneCount (عدد العناصر المكتملة)
    5. عرض قائمة المهام الكاملة + العدد في القالب

    البيانات الأولية: { id: 1, text: 'Learn Vue 3', done: false }

Web-Tutorial.com

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

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

100%