ثور Vue 3 طريقة بناء المطورين للواجهات التفاعلية والأداء العالي. Composition API، دعم TypeScript المحسن، والميزات الجديدة مثل Teleport و Suspense مهدت الطريق لتطوير تطبيقات أكثر قابلية للتوسع، قابلية للصيانة، وكفاءة. إتقان أنماط Vue 3 الأساسية أمر حاسم لأي مطور يريد بناء تطبيقات ويب حديثة. يتعمق هذا الدليل في الأنماط والممارسات الأساسية الرئيسية.
1. إتقان Composition API#
Composition API هو حجر الأساس لتطوير Vue 3 الحديث، حيث يوفر طريقة مرنة لتنظيم وإعادة استخدام المنطق.
- دالة
setup(): نقطة الدخول لاستخدام Composition API داخل المكونات. هناك حيث تعلن الحالة التفاعلية، الخصائص المحسوبة، المراقبين، وخطافات دورة الحياة. - التفاعل مع
refوreactive:ref(): يُستخدم لإنشاء مراجع تفاعلية للقيم البدائية (String، Number، Boolean) أو كائنات فردية.reactive(): يُستخدم لإنشاء وكلاء تفاعليين للكائنات. يتم تتبع التغييرات في خصائص الكائن.
- الخصائص المحسوبة (
computed): لاشتقاق الحالة بناءً على مصادر تفاعلية أخرى. يتم تخزينها مؤقتاً وإعادة تقييمها فقط عندما تتغير تبعياتها. - المراقبون (
watchوwatchEffect):watch(): مراقبة مصدر تفاعلي محدد صراحة وتشغيل رد الاتصال عند تغييره. يوفر تحكماً أكبر في التبعيات والتوقيت.watchEffect(): يشغل دالة فوراً ويعيد تشغيلها كلما تغيرت أي من تبعياتها التفاعلية. أبسط للتأثيرات الجانبية المباشرة.
<script setup lang="ts">
import { ref, reactive, computed, watch, watchEffect } from "vue";
// الحالة التفاعلية
const count = ref(0);
const user = reactive({ name: "John Doe", age: 30 });
// الخاصية المحسوبة
const doubledCount = computed(() => count.value * 2);
// المراقب
watch(count, (newCount, oldCount) => {
console.log(`Count changed from ${oldCount} to ${newCount}`);
});
// مراقب التأثير
watchEffect(() => {
console.log(`Count is ${count.value}`);
});
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Doubled: {{ doubledCount }}</p>
<button @click="count++">Increment</button>
</div>
</template>
2. إدارة الحالة مع Pinia#
Pinia هو حل إدارة الحالة الرسمي لـ Vue 3، ويوفر طريقة حديثة وبديهية لإدارة الحالة العامة.
إنشاء مخزن Pinia#
// stores/counter.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
// الحالة
const count = ref(0)
const name = ref('Counter')
// الجيترات
const doubleCount = computed(() => count.value * 2)
// الإجراءات
function increment() {
count.value++
}
function decrement() {
count.value--
}
function reset() {
count.value = 0
}
return {
count,
name,
doubleCount,
increment,
decrement,
reset
}
})
استخدام المخزن في المكونات#
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
</script>
<template>
<div>
<p>Count: {{ counterStore.count }}</p>
<p>Double: {{ counterStore.doubleCount }}</p>
<button @click="counterStore.increment">+</button>
<button @click="counterStore.decrement">-</button>
<button @click="counterStore.reset">Reset</button>
</div>
</template>
3. أنماط المكونات المتقدمة#
نمط المكونات المركبة (Composite Components)#
<!-- components/FormField.vue -->
<script setup lang="ts">
import { computed } from 'vue'
interface Props {
label: string
modelValue: string
error?: string
}
const props = defineProps<Props>()
const emit = defineEmits<{
'update:modelValue': [value: string]
}>()
const inputClasses = computed(() => ({
'border-red-500': !!props.error,
'border-gray-300': !props.error
}))
</script>
<template>
<div class="form-field">
<label class="block text-sm font-medium text-gray-700">
{{ label }}
</label>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
:class="inputClasses"
class="mt-1 block w-full rounded-md border px-3 py-2 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500"
/>
<p v-if="error" class="mt-1 text-sm text-red-600">
{{ error }}
</p>
</div>
</template>
نمط Renderless Components#
<!-- components/MouseTracker.vue -->
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
const x = ref(0)
const y = ref(0)
const updateMouse = (event: MouseEvent) => {
x.value = event.clientX
y.value = event.clientY
}
onMounted(() => {
document.addEventListener('mousemove', updateMouse)
})
onUnmounted(() => {
document.removeEventListener('mousemove', updateMouse)
})
</script>
<template>
<slot :x="x" :y="y" />
</template>
4. إدارة دورة الحياة المتقدمة#
استخدام Composition Functions#
// composables/useLocalStorage.ts
import { ref, watch } from 'vue'
export function useLocalStorage<T>(key: string, defaultValue: T) {
const value = ref<T>(defaultValue)
// تحميل من localStorage
const stored = localStorage.getItem(key)
if (stored) {
try {
value.value = JSON.parse(stored)
} catch {
value.value = defaultValue
}
}
// حفظ في localStorage
watch(value, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
return value
}
إدارة الذاكرة والتنظيف#
// composables/useEventListener.ts
import { onMounted, onUnmounted } from 'vue'
export function useEventListener(
target: EventTarget,
event: string,
callback: EventListener
) {
onMounted(() => {
target.addEventListener(event, callback)
})
onUnmounted(() => {
target.removeEventListener(event, callback)
})
}
5. تحسين الأداء#
استخدام shallowRef و shallowReactive#
import { shallowRef, shallowReactive } from 'vue'
// للكائنات الكبيرة التي لا تحتاج تفاعل عميق
const largeObject = shallowReactive({ /* ... */ })
// للمراجع التي تحتوي على كائنات
const data = shallowRef({ /* ... */ })
تحسين العرض مع v-memo#
<template>
<div v-for="item in items" :key="item.id" v-memo="[item.id, item.updatedAt]">
<!-- محتوى المكون -->
</div>
</template>
استخدام nextTick بحكمة#
import { nextTick } from 'vue'
const updateUI = async () => {
// تحديث البيانات
data.value = newData
// انتظار العرض
await nextTick()
// الآن يمكن الوصول إلى DOM المحدث
scrollToElement()
}
6. تكامل TypeScript المتقدم#
واجهات المكونات المكتوبة#
<script setup lang="ts">
interface User {
id: number
name: string
email: string
}
interface Props {
user: User
loading?: boolean
}
interface Emits {
(e: 'update', user: User): void
(e: 'delete', id: number): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
</script>
أنواع Pinia المكتوبة#
// stores/user.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
interface User {
id: number
name: string
email: string
}
export const useUserStore = defineStore('user', () => {
const currentUser = ref<User | null>(null)
const users = ref<User[]>([])
const setCurrentUser = (user: User) => {
currentUser.value = user
}
const addUser = (user: User) => {
users.value.push(user)
}
return {
currentUser,
users,
setCurrentUser,
addUser
}
})
7. أنماط الاختبار#
اختبار المكونات مع Vue Test Utils#
// tests/components/Counter.test.ts
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import Counter from '@/components/Counter.vue'
describe('Counter', () => {
it('increments count when button is clicked', async () => {
const wrapper = mount(Counter)
const button = wrapper.find('button')
await button.trigger('click')
expect(wrapper.text()).toContain('1')
})
})
اختبار Composables#
// tests/composables/useCounter.test.ts
import { describe, it, expect } from 'vitest'
import { useCounter } from '@/composables/useCounter'
describe('useCounter', () => {
it('increments count', () => {
const { count, increment } = useCounter()
increment()
expect(count.value).toBe(1)
})
})
8. أنماط الهيكلة#
هيكل المشروع الموصى به#
src/
├── components/
│ ├── ui/ # مكونات UI الأساسية
│ ├── forms/ # مكونات النماذج
│ └── layouts/ # مكونات التخطيط
├── composables/ # دوال التكوين
├── stores/ # مخازن Pinia
├── types/ # أنواع TypeScript
├── utils/ # دوال المساعدة
└── views/ # صفحات التطبيق
نمط الميزات (Feature-based structure)#
src/
├── features/
│ ├── auth/
│ │ ├── components/
│ │ ├── composables/
│ │ ├── stores/
│ │ └── types/
│ └── dashboard/
│ ├── components/
│ ├── composables/
│ ├── stores/
│ └── types/
9. معالجة الأخطاء والاستثناءات#
استخدام Error Boundaries#
<!-- components/ErrorBoundary.vue -->
<script setup lang="ts">
import { ref } from 'vue'
const error = ref<Error | null>(null)
const handleError = (err: Error) => {
error.value = err
// تسجيل الخطأ أو إرساله إلى خدمة التتبع
}
</script>
<template>
<div v-if="error" class="error-boundary">
<h2>حدث خطأ غير متوقع</h2>
<p>{{ error.message }}</p>
<button @click="error = null">إعادة المحاولة</button>
</div>
<slot v-else />
</template>
معالجة الأخطاء في Composables#
// composables/useAsyncData.ts
import { ref, computed } from 'vue'
export function useAsyncData<T>(fetcher: () => Promise<T>) {
const data = ref<T | null>(null)
const loading = ref(false)
const error = ref<string | null>(null)
const execute = async () => {
loading.value = true
error.value = null
try {
data.value = await fetcher()
} catch (err) {
error.value = err instanceof Error ? err.message : 'حدث خطأ غير معروف'
} finally {
loading.value = false
}
}
return {
data: computed(() => data.value),
loading: computed(() => loading.value),
error: computed(() => error.value),
execute
}
}
10. تحسين تجربة المطور#
استخدام Vue DevTools#
Vue DevTools هو أداة أساسية لتصحيح أخطاء تطبيقات Vue 3:
- فحص شجرة المكونات
- مراقبة الحالة التفاعلية
- تتبع أحداث المكونات
- تحليل أداء العرض
إعداد التطوير الفعال#
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
hmr: true
},
build: {
sourcemap: true
}
})
الخاتمة#
Vue 3 يوفر مجموعة قوية من الأدوات والأنماط لبناء تطبيقات ويب حديثة وقابلة للتوسع. من خلال إتقان Composition API، Pinia لإدارة الحالة، والأنماط المتقدمة، يمكنك بناء تطبيقات أكثر صيانة، أداءً، وتجربة مطور أفضل.
تذكر أن أفضل الممارسات تتطور مع الإطار. ابق على اطلاع دائم بأحدث التطورات في مجتمع Vue، وطبق هذه الأنماط تدريجياً في مشاريعك. التوازن بين الكود النظيف، الأداء، وقابلية الصيانة هو مفتاح بناء تطبيقات Vue 3 ناجحة في 2025. 🚀
