مقدمة
مرحبا بك في الجزء الأول من “نانوستورز في الأسترو: مغامرة متعددة الإطارات” السلسلة. إذا كنت قد تعاني من إدارة الحالة في مشاريعك الأسترو المتعددة الإطارات، فإنك في طريق المتعددة التي تنتظرك. اليوم ، نستكشف نانوستورز، حلولة إدارة خفيفة الوزن التي تتم دمجها بشكل سلس مع هيكلة جزيرات المكونات التي تتوافر في Astro.
في هذه المقالة سنغوص في كيفية توفير Nanostores لتسريع إدارة الحالة عبر Astro و React و Vue و Svelte المكونات. سنتعامل بالحالة المستقلة والحالة المشتركة وسنقدم إدارة الحالة الدائمة. دعونا نستكمل هذه الرحلة لتبسيط إدارة الحالة في مشاريع أسترو الخاصة بنا.
الخلاصة
إذا كنت ترغب في الغوص فيها بشكل مباشر، هذه توصيف سريع وبعض الروابط الأساسية:
-
هذه المقالة تستكشف Nanostores لإدارة الحالة في مشاريع الأسترو المتعددة الإطارات.
-
نتعامل بالحالة المستقلة والحالة المشتركة ونقدم إدارة الحالة الدائمة عبر Astro و React و Vue و Svelte المكونات.
-
اشهر معلومات الموارد:
تعلم بحرية التسويق والتنسيق مع التجريدات والشفرات في هذا المقال لتختبر التعلم العميق!
فهم Nanostores
ما هو التجريد الصغير؟
تعتبر Nanostores مكتبة إدارة حالة بسيطة مصممة لتكون مستقلة عن الأطر. توفر واجهة برمجة تطبيقات مباشرة لإنشاء وإدارة قطع صغيرة من الحالة التي يمكن مشاركتها وتحديثها بسهولة عبر أجزاء مختلفة من التطبيق الخاص بك.
لماذا تستخدم Nanostores في Astro؟
-
خفيفة وسريعة: Nanostores صغيرة للغاية (بين 265 و814 بايت)، مما يضمن عدم تضخيم حجم الحزمة الخاصة بك.
-
مستقلة عن الأطر: مثالية لبيئة Astro متعددة الأطر. تتكامل بسلاسة مع React وVue وSvelte وSolid وجافا سكريبت العادية.
-
واجهة برمجة تطبيقات بسيطة: لا إعدادات معقدة أو قوالب ثابتة. فهي مباشرة وبديهية الاستخدام.
-
تكامل مع جزر المكونات في Astro: تعزز Nanostores من بنية الجزر في Astro، مما يسمح بإدارة الحالة بكفاءة عبر المكونات التفاعلية المعزولة.
المفاهيم الأساسية
تدور Nanostores حول ثلاثة مفاهيم رئيسية:
-
ذرات: مخازن بسيطة تحتوي على قيمة واحدة.
-
خرائط: مخازن تحتوي على كائنات ذات خصائص متعددة.
-
مخازن محسوبة: مخازن مشتقة تحسب قيمتها بناءً على مخازن أخرى.
لنلقِ نظرة سريعة على مثال:
import { atom, map, computed } from 'nanostores'
// Atom
const count = atom(0)
// Map
const user = map({ name: 'Astro Fan', isLoggedIn: false })
// Computed Store
const greeting = computed([user], (user) =>
user.isLoggedIn ? `Welcome back, ${user.name}!` : 'Hello, guest!'
)
في هذا المقتطف، أنشأنا ذرة لعداد، وخريطة لبيانات المستخدم، ومخزن محسوب لتحية ديناميكية. بسيط، أليس كذلك؟
إعداد Nanostores في مشروع Astro
البدء مع Nanostores في مشروع Astro الخاص بك بسيط. إليك كيفية القيام بذلك:
- أولاً، قم بتثبيت Nanostores وتكاملاتها مع الإطار:
# Using npm
npm install nanostores
npm install @nanostores/react # For React
npm install @nanostores/vue # For Vue
# Using yarn
yarn add nanostores
yarn add @nanostores/react # For React
yarn add @nanostores/vue # For Vue
# Using pnpm
pnpm add nanostores
pnpm add @nanostores/react # For React
pnpm add @nanostores/vue # For Vue
# Note: Svelte doesn't require a separate integration
- قم بإنشاء ملف جديد لمخازنك، لنقل
src/stores/counterStore.js
:
import { atom } from 'nanostores'
export const count = atom(0)
export function increment() {
count.set(count.get() + 1)
}
export function decrement() {
count.set(count.get() - 1)
}
- الآن يمكنك استخدام هذا المخزن في أي من مكوناتك. إليك مثال سريع في مكون Astro:
---
import { count, increment, decrement } from '../stores/counterStore'
---
<div>
<button onclick={decrement}>-</button>
<span>{count.get()}</span>
<button onclick={increment}>+</button>
</div>
<script>
import { count } from '../stores/counterStore'
count.subscribe(value => {
document.querySelector('span').textContent = value
})
</script>
وهكذا! لقد قمت بإعداد Nanostore في مشروع Astro الخاص بك.
إدارة حالة مستقلة
في مشاريع Astro متعددة الأطر، قد ترغب في إدارة الحالة بشكل مستقل داخل مكونات الأطر المختلفة. يتيح لك Nanostores القيام بذلك بسلاسة. دعنا نستعرض كيفية تنفيذ إدارة حالة مستقلة عبر مكونات React، Vue، Svelte، وAstro.
مثال على عداد
سنقوم بتنفيذ عداد بسيط في كل إطار لإظهار إدارة الحالة المستقلة.
أولاً، دعنا ننشئ مخزن عداد مستقل:
// src/stores/independentCounterStore.js
import { atom } from 'nanostores'
export const reactCount = atom(0)
export const vueCount = atom(0)
export const svelteCount = atom(0)
export const astroCount = atom(0)
export function increment(store) {
store.set(store.get() + 1)
}
export function decrement(store) {
store.set(store.get() - 1)
}
الآن، دعنا ننفذ هذا العداد في كل إطار:
عداد React
// src/components/ReactCounter.jsx
import { useStore } from '@nanostores/react'
import { reactCount, increment, decrement } from '../stores/independentCounterStore'
export function ReactCounter() {
const count = useStore(reactCount)
return (
<div>
<button onClick={() => decrement(reactCount)}>-</button>
<span>{count}</span>
<button onClick={() => increment(reactCount)}>+</button>
</div>
)
}
عداد Vue
<!-- src/components/VueCounter.vue -->
<template>
<div>
<button @click="decrement(vueCount)">-</button>
<span>{{ count }}</span>
<button @click="increment(vueCount)">+</button>
</div>
</template>
<script setup>
import { useStore } from '@nanostores/vue'
import { vueCount, increment, decrement } from '../stores/independentCounterStore'
const count = useStore(vueCount)
</script>
عداد Svelte
<!-- src/components/SvelteCounter.svelte -->
<script>
import { svelteCount, increment, decrement } from '../stores/independentCounterStore'
</script>
<div>
<button on:click={() => decrement(svelteCount)}>-</button>
<span>{$svelteCount}</span>
<button on:click={() => increment(svelteCount)}>+</button>
</div>
عداد Astro
---
import { astroCount, increment, decrement } from '../stores/independentCounterStore'
---
<div>
<button id="decrement">-</button>
<span id="count">{astroCount.get()}</span>
<button id="increment">+</button>
</div>
<script>
import { astroCount, increment, decrement } from '../stores/independentCounterStore'
document.getElementById('decrement').addEventListener('click', () => decrement(astroCount))
document.getElementById('increment').addEventListener('click', () => increment(astroCount))
astroCount.subscribe(value => {
document.getElementById('count').textContent = value
})
</script>
كما ترى، يحتفظ كل مكون من مكونات الإطار بحالته المستقلة الخاصة باستخدام Nanostores. يتيح لك هذا النهج إدارة الحالة بشكل معزول داخل كل مكون، بغض النظر عن الإطار المستخدم.
الحالة المشتركة عبر الأطر
الآن، دعنا نستعرض كيف يتيح Nanostores حالة مشتركة عبر مكونات الإطار المختلفة. هذا مفيد بشكل خاص عندما تحتاج إلى مزامنة الحالة بين أجزاء متعددة من تطبيقك.
مثال على عداد مشترك
سننشئ عدادًا مشتركًا يمكن تحديثه وعرضه عبر مكونات React، Vue، Svelte، وAstro.
أولاً، دعنا ننشئ مخزن عداد مشترك:
// src/stores/sharedCounterStore.js
import { atom } from 'nanostores'
export const sharedCount = atom(0)
export function increment() {
sharedCount.set(sharedCount.get() + 1)
}
export function decrement() {
sharedCount.set(sharedCount.get() - 1)
}
الآن، دعنا ننفذ مكونات في كل إطار تستخدم هذه الحالة المشتركة:
عداد React مشترك
// src/components/ReactSharedCounter.jsx
import { useStore } from '@nanostores/react'
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
export function ReactSharedCounter() {
const count = useStore(sharedCount)
return (
<div>
<h2>React Shared Counter</h2>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
)
}
عداد Vue مشترك
<!-- src/components/VueSharedCounter.vue -->
<template>
<div>
<h2>Vue Shared Counter</h2>
<button @click="decrement">-</button>
<span>{{ count }}</span>
<button @click="increment">+</button>
</div>
</template>
<script setup>
import { useStore } from '@nanostores/vue'
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
const count = useStore(sharedCount)
</script>
عداد Svelte مشترك
<!-- src/components/SvelteSharedCounter.svelte -->
<script>
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
</script>
<div>
<h2>Svelte Shared Counter</h2>
<button on:click={decrement}>-</button>
<span>{$sharedCount}</span>
<button on:click={increment}>+</button>
</div>
عداد Astro مشترك
---
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
---
<div>
<h2>Astro Shared Counter</h2>
<button id="shared-decrement">-</button>
<span id="shared-count">{sharedCount.get()}</span>
<button id="shared-increment">+</button>
</div>
<script>
import { sharedCount, increment, decrement } from '../stores/sharedCounterStore'
document.getElementById('shared-decrement').addEventListener('click', decrement)
document.getElementById('shared-increment').addEventListener('click', increment)
sharedCount.subscribe(value => {
document.getElementById('shared-count').textContent = value
})
</script>
مع هذا الإعداد، ستشارك جميع هذه المكونات نفس حالة العداد. زيادة أو تقليل العداد في أي مكون سيقوم بتحديث القيمة عبر جميع المكونات، بغض النظر عن الإطار المستخدم.
إدارة الحالة الدائمة
بينما تكون الحالات المستقلة والمشتركة قوية، في بعض الأحيان نحتاج إلى أن تستمر حالتنا عبر إعادة تحميل الصفحة أو حتى جلسات المتصفح. هنا يأتي دور @nanostores/persistent
. دعونا نستكشف كيفية تنفيذ الحالة الدائمة في مشروع Astro الخاص بنا.
إعداد الحالة الدائمة
أولاً، نحتاج إلى تثبيت الإضافة الدائمة لـ Nanostores:
# Using npm
npm install @nanostores/persistent
# Using yarn
yarn add @nanostores/persistent
# Using pnpm
pnpm add @nanostores/persistent
الآن، دعونا ننشئ عداد دائم سيحافظ على قيمته حتى عند تحديث الصفحة:
// src/stores/persistentCounterStore.js
import { persistentAtom } from '@nanostores/persistent'
export const persistentCount = persistentAtom('persistentCount', 0)
export function increment() {
persistentCount.set(persistentCount.get() + 1)
}
export function decrement() {
persistentCount.set(persistentCount.get() - 1)
}
export function reset() {
persistentCount.set(0)
}
في هذا المثال، ‘persistentCount’ هو المفتاح المستخدم لتخزين القيمة في localStorage، و 0 هي القيمة الأولية.
مثال على العداد الدائم متعدد الإطارات
دعونا ننفذ عداد دائم باستخدام مكونات من إطارات مختلفة. هذا العداد سيحافظ على قيمته عبر إعادة تحميل الصفحة وسيكون قابلاً للوصول من أي إطار.
عداد دائم باستخدام React (زيادة)
// src/components/ReactPersistentIncrement.jsx
import { useStore } from '@nanostores/react'
import { persistentCount, increment } from '../stores/persistentCounterStore'
export function ReactPersistentIncrement() {
const count = useStore(persistentCount)
return (
<button onClick={increment}>
React Increment: {count}
</button>
)
}
عداد دائم باستخدام Vue (نقصان)
<!-- src/components/VuePersistentDecrement.vue -->
<template>
<button @click="decrement">
Vue Decrement: {{ count }}
</button>
</template>
<script setup>
import { useStore } from '@nanostores/vue'
import { persistentCount, decrement } from '../stores/persistentCounterStore'
const count = useStore(persistentCount)
</script>
عداد دائم باستخدام Svelte (عرض)
<!-- src/components/SveltePersistentDisplay.svelte -->
<script>
import { persistentCount } from '../stores/persistentCounterStore'
</script>
<div>
Svelte Display: {$persistentCount}
</div>
عداد دائم باستخدام Astro (إعادة تعيين)
---
import { reset } from '../stores/persistentCounterStore'
---
<button id="reset-button">Astro Reset</button>
<script>
import { persistentCount, reset } from '../stores/persistentCounterStore'
document.getElementById('reset-button').addEventListener('click', reset)
persistentCount.subscribe(value => {
console.log('Persistent count updated:', value)
})
</script>
الآن، يمكنك استخدام هذه المكونات معًا في صفحة Astro:
---
import ReactPersistentIncrement from '../components/ReactPersistentIncrement'
import VuePersistentDecrement from '../components/VuePersistentDecrement.vue'
import SveltePersistentDisplay from '../components/SveltePersistentDisplay.svelte'
---
<div>
<h2>Persistent Counter Across Frameworks</h2>
<ReactPersistentIncrement client:load />
<VuePersistentDecrement client:load />
<SveltePersistentDisplay client:load />
<button id="reset-button">Astro Reset</button>
</div>
<script>
import { persistentCount, reset } from '../stores/persistentCounterStore'
document.getElementById('reset-button').addEventListener('click', reset)
persistentCount.subscribe(value => {
console.log('Persistent count updated:', value)
})
</script>
هذا الإعداد يُظهر عدادًا دائمًا حيث:
-
React يتعامل مع الزيادة
-
Vue يتعامل مع النقصان
-
يعرض Svelte العدد الحالي
-
يوفر Astro زر إعادة الضبط
ستستمر قيمة العداد عبر إعادة تحميل الصفحات، مما يُظهر قوة @nanostores/persistent
في الحفاظ على الحالة.
حالات استخدام الحالة المستمرة
الحالة المستمرة مفيدة بشكل خاص لـ:
-
تفضيلات المستخدم (مثل إعدادات الثيم، خيارات اللغة)
-
النماذج المكتملة جزئيًا (لمنع فقدان البيانات عند تحديث الصفحة عن طريق الخطأ)
-
رموز المصادقة (للحفاظ على جلسات المستخدم)
-
ذاكرة التخزين المؤقت المحلية للبيانات التي يتم الوصول إليها بشكل متكرر
من خلال الاستفادة من @nanostores/persistent
، يمكنك تحسين تجربة المستخدم عن طريق الحفاظ على بيانات الحالة الهامة عبر تحميل الصفحات وجلسات المتصفح.
أفضل الممارسات والنصائح
عند دمج Nanostores في مشاريع Astro الخاصة بك، ضع في اعتبارك أفضل الممارسات والنصائح هذه لتحقيق أقصى استفادة من حل إدارة الحالة خفيف الوزن هذا.
1. اختر نوع المتجر المناسب
-
استخدم
atom
لحالات القيم الفردية البسيطة. -
استخدم
map
للحالات الشبيهة بالكائنات التي تحتوي على خصائص متعددة. -
استخدم
computed
للحالات المشتقة التي تعتمد على متاجر أخرى. -
استخدم
persistentAtom
أوpersistentMap
عندما تحتاج إلى الاحتفاظ بالحالة عبر عمليات إعادة تحميل الصفحة.
2. حافظ على المتاجر صغيرة وتركّز على الهدف
بدلاً من إنشاء متاجر كبيرة وشاملة، يُفضل استخدام متاجر أصغر وأكثر تركيزًا. هذه الطريقة تحسّن من سهولة الصيانة والأداء من خلال السماح بتحديثات أكثر تفصيلاً.
// Prefer this:
const userProfile = map({ name: '', email: '' })
const userPreferences = map({ theme: 'light', language: 'en' })
// Over this:
const user = map({ name: '', email: '', theme: 'light', language: 'en' })
3. استخدم المتاجر المشتقة للحالات المشتقة
عندما يكون لديك حالات تعتمد على حالات أخرى، استخدم المتاجر المشتقة. هذا يساعد على الحفاظ على مبدأ DRY (Don’t Repeat Yourself) ويضمن أن الحالة المشتقة تكون دائمًا محدّثة.
import { atom, computed } from 'nanostores'
const firstName = atom('John')
const lastName = atom('Doe')
const fullName = computed(
[firstName, lastName],
(first, last) => `${first} ${last}`
)
4. استفد من TypeScript لضمان سلامة الأنواع
تدعم Nanostores TypeScript بشكل ممتاز. استخدمها لاكتشاف الأخطاء مبكرًا وتحسين تجربة المطور.
import { atom } from 'nanostores'
interface User {
id: number
name: string
}
const currentUser = atom<User | null>(null)
5. راعِ الأداء في التطبيقات الكبيرة
على الرغم من أن Nanostores خفيفة الوزن، كن حذرًا بشأن الأداء في التطبيقات الكبيرة. استخدم دالة batched
لتجميع تحديثات المتاجر معًا، مما يقلل من عدد عمليات إعادة العرض.
import { atom, batched } from 'nanostores'
const count1 = atom(0)
const count2 = atom(0)
export const incrementBoth = batched(() => {
count1.set(count1.get() + 1)
count2.set(count2.get() + 1)
})
6. حافظ على منطق الإطار المحدد منفصلًا
عند استخدام Nanostores في مشروع Astro متعدد الأطر، حاول الحفاظ على منطق الحالة الأساسي غير متعلق بأي إطار معين. هذا يجعل من السهل مشاركة الحالة بين المكونات الخاصة بأطر مختلفة.
// stores/themeStore.js
import { atom } from 'nanostores'
export const theme = atom('light')
export function toggleTheme() {
theme.set(theme.get() === 'light' ? 'dark' : 'light')
}
// React component
import { useStore } from '@nanostores/react'
import { theme, toggleTheme } from '../stores/themeStore'
function ThemeToggle() {
const currentTheme = useStore(theme)
return <button onClick={toggleTheme}>{currentTheme}</button>
}
7. استخدم المتاجر الدائمة بحكمة
على الرغم من أن المتاجر الدائمة قوية، استخدمها بعناية. ليس كل حالة بحاجة إلى الاستمرارية بين الجلسات. الإفراط في استخدام المتاجر الدائمة قد يؤدي إلى سلوك غير متوقع ومشاكل في الأداء.
8. تصحيح أخطاء Nanostores
لتسهيل تصحيح الأخطاء، يمكنك استخدام دالة onMount
لتسجيل تغييرات الحالة:
import { atom, onMount } from 'nanostores'
const count = atom(0)
if (import.meta.env.DEV) {
onMount(count, () => {
count.listen((value) => {
console.log('Count changed:', value)
})
})
}
9. تنظيف الاشتراكات
عند استخدام Nanostores في مكونات يمكن إلغاء تحميلها، تأكد من تنظيف الاشتراكات لمنع تسرب الذاكرة.
import { useEffect } from 'react'
import { count } from '../stores/countStore'
function Counter() {
useEffect(() => {
const unsubscribe = count.subscribe(() => {
// Do something
})
return unsubscribe
}, [])
// Rest of the component
}
باتباع هذه الممارسات والنصائح الأفضل، ستتمكن من إدارة الحالة بفعالية في مشاريع Astro الخاصة بك باستخدام Nanostores، بغض النظر عن الأطر التي تقوم بدمجها.
الخاتمة
كما استعرضنا خلال هذا المقال، يوفر Nanostores حلاً قويًا وخفيفًا لإدارة الحالة في مشاريع Astro، خاصة عند العمل مع أطر متعددة. لنلخص النقاط الرئيسية:
- التعددية: يندمج Nanostores بسلاسة مع Astro وReact وVue وSvelte وSolid، مما يجعله خيارًا مثاليًا لمشاريع الأطر المتعددة.
-
بسيط
: مع واجهة API بسيطة، يقدم نانوستورز منحنى درجة تعلم منخفض بعدم إزالة قدرات إدارة الحالة القوية.
- المرونة: من المتجرد والمخزونات الذرية إلى الحالات المحاسبة المعقدة وحتى التخزين الدائمي، يتكيف نانوستورز مع مجموعة واسعة من حاجيات إدارة الحالة.
- الأداء: واجهته خفيفة الوزن تضمن أن نانوستورز لن يتم تضخم تطبيقك، مما يحافظ على مزايا الأسترو.
- أفضل الممارسات: من خلال الاتباع النصوص التي نقاشنا عنها، مثل إبعاد المخزونات عن الكبيرة والتركيز، والتسخير للتيبيسكريبت واستخدام المخزونات المحاسبة للحالات المنسجمة، يمكنك أن تنشئ أنظمة إدارة الحالة مناسبة وفعالة.
تبرز نانوستورز في هيكلة الجزر المكونات لـ Astro ، مما يسمح لك بإدارة الحالة عبر المكونات التفاعلية المعزولة بكفاءة. سواء كنت تقوم ببناء موقع بسيط ببعض العناصر التفاعلية القليلة أو تطوير تطبيق ويب معقد بالميزانيات المتعددة ، توفر نانوستورز الأدوات التي تحتاجها للتعامل مع الحالة بالفعل.
وفي ما تستمر رحلتك مع Astro ونانوستورز ، تذكر أن أفضل طريقة للتعلم هي من خلال القيام بالعمل. تجربة أنواع مختلفة من المخزونات ، وتحقيق توفير الحالة المشتركة عبر أنظمة الميزانيات المتعددة ، واستكشاف إمكانيات تخزين البيانات الدائمي. كل مشروع سيحصل على تحديات جديدة وفرص لتحسين مهاراتك في إدارة الحالة.
أنتظروا لمقالات قادمة في سلسلة “نانوستورز في Astro: مغامرة مع الأنظمة المتعددة” حيث سنغوص أعمق في تطبيقاتنا العملية وتقنيات متقدمة لإدارة الحالة في مشاريع Astro.
مصادر إضافية
لتعميم فهمك لنانوستورز واستخدامها في مشاريع Astro ، انظر إلى هذه المصادر القيمة:
- مشروع العرض الديمو على GitHub
- موقع العرض الحي
- توثيق Astro حول توزيع الحالة
- توثيق نانوستورز الرسمي
- قاعدة البيانات الدائمة لمجموعة GitHub لـ Nanostores
تواصل البرمجيات سعيدًا، و حظي مشاريعك الفضائية بالدوام و بالكفاءة!
Source:
https://meirjc.hashnode.dev/state-management-in-astro-a-deep-dive-into-nanostores