اخبار وب

Svelte 5 و Runes؛ state، داده مشتق‌شده و مهاجرت

راهنمای Svelte 5 و Runes با نمونه کامل جست‌وجوی مقاله؛ $state، $derived، $effect و $props، مرز مالکیت داده و نکات مهاجرت از Svelte 4.

1404/11/15 2 دقیقه مطالعه 944 بازدید
Svelte 5 و Runes؛ state، داده مشتق‌شده و مهاجرت

Svelte 5 با Runes شکل روشن‌تری برای تعریف state و داده مشتق‌شده ارائه می‌کند. به جای آنکه بخشی از رفتار واکنش‌پذیری فقط از جای قرار گرفتن متغیر در فایل فهمیده شود، دستورهایی مثل $state و $derived نقش مقدار را مشخص می‌کنند. این تغییر برای اشتراک منطق و فهم وابستگی‌های داده اهمیت دارد. اما وجود Runes به معنی سریع‌تر شدن قطعی همه برنامه‌ها یا حذف نیاز به طراحی state نیست.

انتشار پایدار اولیه Svelte 5 در اکتبر ۲۰۲۴ انجام شد؛ این موضوع را با یک خبر تازه سال ۲۰۲۶ اشتباه نگیرید. معرفی رسمی Svelte 5 مسیر تغییر را توضیح می‌دهد. برای پروژه امروز، مستندات و نسخه‌های مورد استفاده را مبنا قرار دهید. در این مقاله، مفهوم Runes، نمونه قابل اجرا و نکته‌های مهاجرت را بررسی می‌کنیم و از رتبه‌بندی بدون معیار میان فریم‌ورک‌ها پرهیز می‌کنیم.

Rune یک تابع معمول JavaScript نیست

Runes دستورهای شناخته‌شده توسط کامپایلر Svelte هستند و با ظاهری شبیه فراخوانی تابع نوشته می‌شوند. برای استفاده از آن‌ها در کامپوننت، مانند یک ابزار عادی از بسته‌ای import نمی‌کنید. کامپایلر معنای آن‌ها را می‌فهمد و کد لازم را تولید می‌کند. بنابراین نمی‌توان یک فایل دلخواه خارج از زنجیره Svelte را فقط با نوشتن $state واکنش‌پذیر کرد.

این تمایز در مثال و راهنمای اجرا مهم است. اگر نمونه‌ای فقط دو خط state نشان دهد ولی محیط و نوع فایل را معرفی نکند، خواننده ممکن است آن را در یک فایل JavaScript مستقل قرار دهد و انتظار نتیجه داشته باشد. در نمونه این مقاله، پروژه Svelte و فایل App.svelte مشخص‌اند. کد HTML، style و منطق داده نیز کنار هم آمده‌اند تا وابستگی پنهان باقی نماند.

$state برای داده‌ای است که تغییر می‌کند

$state مقدار واکنش‌پذیر ایجاد می‌کند. در مثال، عبارت جست‌وجو و وضعیت checkbox تغییر می‌کنند، پس state هستند. آرایه ثابت مقاله‌ها را معمولی نگه داشته‌ایم، چون نمونه آن را ویرایش نمی‌کند. واکنش‌پذیر کردن تمام مقدارها بدون نیاز، فهم قرارداد داده را بهتر نمی‌کند. نخست مشخص کنید چه چیزی از تعامل یا پاسخ سرویس تغییر می‌کند و چه چیزی ورودی ثابت محاسبه است.

در راهنمای $state، رفتار مقدارهای ساده و ساختارهای داده توضیح داده شده است. برای اشیای ساده و آرایه‌ها، Proxyهای واکنش‌پذیر بخشی از مدل هستند؛ با این حال همه نوع‌های شیء را نباید یکسان فرض کرد. وقتی state را از مرز تابع یا کتابخانه عبور می‌دهید، رفتار همان قرارداد و نوع داده را بررسی کنید.

$derived برای نتیجه محاسبه است

فهرست نتیجه در نمونه از عبارت جست‌وجو، وضعیت فیلتر و مقاله‌ها به دست می‌آید. لازم نیست آن را به‌عنوان state دوم نگه داریم و هر بار دستی با ورودی هماهنگ کنیم. $derived رابطه را مشخص می‌کند. این تفکیک جلوی یک گروه خطا را می‌گیرد: state اصلی تغییر کرده ولی نسخه کپی نتیجه هنوز قدیمی مانده است.

محاسبه derived بهتر است بدون اثر جانبی باشد. فیلتر کردن آرایه و برگرداندن نتیجه با ارسال درخواست یا تغییر داده دیگری یکسان نیست. اگر در محاسبه، state دیگری را تغییر دهید، مسیر وابستگی مبهم می‌شود. مستندات $derived مدل وابستگی و استفاده را توضیح می‌دهند. از آن برای بیان نتیجه یک محاسبه استفاده کنید، نه پنهان کردن یک جریان کاری چندمرحله‌ای.

نمونه کامل فهرست و جست‌وجو

این نمونه سه مقاله محلی دارد و درخواست شبکه ارسال نمی‌کند. جست‌وجو با includes روی عنوان انجام می‌شود؛ بنابراین هدف آموزش state است، نه پیاده‌سازی موتور جست‌وجوی فارسی. یک checkbox نتیجه را به دسته توسعه وب محدود می‌کند و حالت خالی هم پیام مشخص دارد. تعداد نتیجه با فهرست واقعی هماهنگ است و برای هر آیتم، id پایدار استفاده شده است.

یک پروژه Vite با قالب Svelte و TypeScript بسازید. نسخه جاری ابزار ساخت را برای ایجاد پروژه استفاده کنید و در پروژه تولیدشده، وابستگی‌ها را نصب کنید. فایل src/App.svelte را با کد کامل پایین جایگزین کنید؛ فایل ورودی و تنظیمات تولیدشده پروژه را نگه دارید. اگر ساختار دیگری انتخاب کرده‌اید، نمونه را در کامپوننت اصلی همان ساختار قرار دهید.

npm create vite@latest svelte-runes-demo -- --template svelte-ts
cd svelte-runes-demo
npm install
<script lang="ts">
  let query = $state('');
  let showOnlyWeb = $state(false);
  const articles = [
    { id: 1, title: 'شروع کار با Svelte', category: 'web' },
    { id: 2, title: 'خواندن بهتر متن فارسی', category: 'design' },
    { id: 3, title: 'مدیریت حالت در رابط', category: 'web' },
  ];
  const visibleArticles = $derived(
    articles.filter(article =>
      article.title.includes(query.trim()) &&
      (!showOnlyWeb || article.category === 'web')
    )
  );
</script>

<main dir="rtl">
  <h1>فهرست مقاله‌ها</h1>
  <label for="query">جست‌وجو در عنوان</label>
  <input id="query" type="search" bind:value={query} />
  <label class="checkbox">
    <input type="checkbox" bind:checked={showOnlyWeb} />
    فقط مقاله‌های توسعه وب
  </label>
  <p role="status">تعداد نتیجه: {visibleArticles.length}</p>
  {#if visibleArticles.length === 0}
    <p>نتیجه‌ای پیدا نشد. عبارت یا فیلتر را تغییر دهید.</p>
  {:else}
    <ul>
      {#each visibleArticles as article (article.id)}
        <li>
          <h2>{article.title}</h2>
          <p>دسته: {article.category === 'web' ? 'توسعه وب' : 'طراحی'}</p>
        </li>
      {/each}
    </ul>
  {/if}
</main>

<style>
  main { max-width: 40rem; margin: 2rem auto; padding: 1rem; font-family: sans-serif; color: #243a32; }
  input[type='search'] { display: block; box-sizing: border-box; width: 100%; padding: .75rem; margin: .5rem 0 1rem; }
  .checkbox { display: flex; align-items: center; gap: .5rem; }
  ul { padding: 0; list-style: none; display: grid; gap: .75rem; }
  li { padding: 1rem; border: 1px solid #a8bdb2; border-radius: .75rem; }
  h2 { margin-top: 0; font-size: 1.15rem; }
</style>

با npm run dev برنامه را اجرا کنید. ابتدا هر سه مقاله دیده می‌شوند؛ با فعال کردن checkbox دو مورد توسعه وب باقی می‌مانند. اگر عبارتی وارد کنید که در عنوان‌ها نیست، پیام نتیجه خالی ظاهر می‌شود. ویژگی bind:value داده ورودی جست‌وجو و bind:checked وضعیت checkbox را هماهنگ می‌کنند. هیچ class یا کامپوننتی بیرون نمونه لازم نیست و CSS همه عناصر معرفی‌شده را پوشش می‌دهد.

در یک محصول واقعی، جست‌وجوی فارسی ممکن است به یکسان‌سازی نویسه‌ها، فاصله‌ها و قواعد مرتب‌سازی نیاز داشته باشد. این کار را با اضافه کردن Runes حل‌شده فرض نکنید. همچنین اگر جست‌وجو از API انجام می‌شود، وضعیت بارگذاری، خطا و پاسخ قدیمی را جدا مدیریت کنید. راهنمای Debounce و Throttle برای کنترل نرخ درخواست و تشخیص رفتار مناسب مفید است.

$effect چه زمانی لازم است؟

$effect برای همگام کردن اثر جانبی با داده واکنش‌پذیر کاربرد دارد؛ مثلاً ارتباط با یک ابزار بیرونی یا مدیریت منبعی که با تغییر state باید به‌روز شود. برای محاسبه فهرست نتیجه در مثال، به آن نیاز نداریم. استفاده از effect برای نگهداری کپی یک مقدار محاسبه‌شده، معمولاً قرارداد ساده derived را به یک جریان دستی تبدیل می‌کند.

اگر effect یک listener یا منبع بیرونی می‌سازد، چرخه پاک‌سازی آن را مشخص کنید. تکرار اجرا بدون cleanup می‌تواند رفتار یا مصرف منابع نامناسب ایجاد کند. همچنین effect را محل قرار دادن هر کدی که «بعداً باید اجرا شود» ندانید. برای عملیات مستقیم کاربر، handler روشن می‌تواند مناسب‌تر باشد؛ برای مقدار مشتق‌شده، derived و برای هماهنگی اثر جانبی، effect را بررسی کنید.

$props و قرارداد کامپوننت

کامپوننت قابل استفاده مجدد باید ورودی‌های روشن داشته باشد. $props در مدل Runes به تعریف این ورودی‌ها مربوط است. بهتر است نام، نوع و مقدار پیش‌فرض هر prop با نقش آن هماهنگ باشد. اگر کامپوننت نتیجه جست‌وجو را نمایش می‌دهد، نباید بی‌دلیل مسئول fetch، ذخیره‌سازی و تصمیم‌های کل صفحه هم باشد. مرز کوچک‌تر، استفاده و تغییر را ساده‌تر می‌کند.

تغییر داده‌ای که از بیرون دریافت شده را نیز با قرارداد مالکیت هماهنگ کنید. اگر چند کامپوننت یک مقدار را مصرف می‌کنند، مسیر تغییر باید معلوم باشد. داشتن Proxy یا ابزار واکنش‌پذیری، مسئله مالکیت state را خودکار حل نمی‌کند. نام propها و callbackها باید نشان دهد چه کسی تصمیم را می‌گیرد و چه کسی فقط نتیجه را نمایش می‌دهد.

مهاجرت از Svelte 4

برای پروژه موجود، راهنمای مهاجرت رسمی نقطه شروع است. قبل از تبدیل همه فایل‌ها، بخش‌های حساس و کتابخانه‌های مورد استفاده را مشخص کنید. تغییر syntax، رفتار داده و قرارداد eventها ممکن است در کنار هم دیده شوند. مهاجرت را با بازطراحی کامل رابط مخلوط نکنید؛ دانستن علت یک تفاوت رفتاری در یک تغییر محدود آسان‌تر است.

در نمونه از onclick یا syntax رویداد قدیمی استفاده نکرده‌ایم چون تعامل با binding انجام می‌شود، اما در مهاجرت handlerها باید قرارداد نسخه جدید را بخوانید. همین موضوع برای props، محاسبه‌های واکنش‌پذیر و الگوهای اشتراک state برقرار است. تبدیل مکانیکی کد می‌تواند کار تکراری را کم کند، ولی فهم وابستگی‌ها همچنان لازم است.

Runes با Stores چه رابطه‌ای دارند؟

معرفی Runes به معنی بی‌استفاده شدن خودکار تمام Stores نیست. پروژه ممکن است به قرارداد store یک کتابخانه یا الگوی موجود تیم وابسته باشد. ابزار را بر اساس نیاز به اشتراک داده، محیط استفاده و چرخه عمر انتخاب کنید. برای یک ورودی محلی در کامپوننت، state ساده کافی است؛ برای منبع مشترک، باید مالکیت و محل ایجاد آن مشخص باشد.

در برنامه دارای رندر سرور، state مشترک را هم با دقت طراحی کنید. داده مربوط به یک کاربر نباید بی‌دلیل به منبع سراسری مشترک میان درخواست‌ها تبدیل شود. محل ایجاد state و زمان پاک شدن آن را با معماری برنامه هماهنگ کنید. یک الگوی مناسب در برنامه تک‌صفحه‌ای محلی ممکن است بدون تغییر برای یک سرویس چندکاربره مناسب نباشد.

عملکرد و تصمیم فریم‌ورک

اگر درباره مهاجرت از React یا Vue فکر می‌کنید، اندازه یک مثال ساده را معیار قطعی کل محصول نگذارید. مهارت تیم، ابزارها، وابستگی‌ها و محیط انتشار هم هزینه دارند. Svelte مدل و ابزارهای خودش را دارد و یادگیری آن می‌تواند مفید باشد، اما تغییر فریم‌ورک فقط برای رسیدن به یک شعار عملکردی، برنامه مهاجرت محسوب نمی‌شود.

برای بررسی عملکرد، از سناریوی واقعی شروع کنید: یک فهرست بزرگ، یک تعامل پرتکرار یا زمان آماده شدن صفحه. سپس سهم کد، داده، شبکه و render را جدا بخوانید. مقاله PageSpeed Insights کمک می‌کند گزارش را به یک عدد کلی محدود نکنید. Runes رابطه داده را روشن‌تر می‌کنند؛ کیفیت تجربه همچنان به تصمیم‌های شما درباره حجم کار و ساختار رابط وابسته است.

Svelte 5 فرصتی برای نوشتن قراردادهای واضح‌تر در state و محاسبه فراهم کرده است. از نمونه کوچک و کامل شروع کنید، مقدار اصلی را از نتیجه مشتق‌شده جدا نگه دارید و effect را برای اثر جانبی واقعی به کار ببرید. این انتخاب‌ها برای نگهداری برنامه ارزش بیشتری دارند تا بازنویسی همه فایل‌ها صرفاً برای داشتن syntax تازه.

واکنش به این مقاله

اشتراک‌گذاری این مقاله:
دیدگاه‌ها 0
۰ / ۲۰۰۰

هنوز دیدگاهی ثبت نشده

اولین نفری باشید که نظر می‌دهد!