Frontend Development

حالت سرور در مقابل حالت کلاینت: انتخاب الگوی مناسب برای برنامه‌های React و Vue

در توسعه فرانت‌اند مدرن، تمایز بین حالت سرور و حالت کلاینت تنها یک مفهوم نظری نیست؛ بلکه ستون فقرات معماری برنامه است. با افزایش پیچیدگی برنامه‌ها، نحوه مدیریت داده‌ها برای عملکرد، قابلیت نگهداری و تجربه کاربری حیاتی می‌شود. چه با React، Vue یا یک چارچوب ترکیبی کار می‌کنید، درک زمان مناسب برای دریافت داده از سرور در مقابل ذخیره آن به صورت محلی، کلید ساخت نرم‌افزار مقیاس‌پذیر است.

تعریف دو حالت

برای ساخت برنامه‌های مؤثر، باید ابتدا به وضوح تعریف کنیم که هر نوع حالت شامل چه چیزی می‌شود. این تمایز تعیین می‌کند که از کدام ابزارها و الگوها باید استفاده کنیم.

حالت سرور به داده‌هایی اشاره دارد که از بک‌اند منشأ می‌گیرند. این شامل پروفایل‌های کاربر، لیست محصولات، توکن‌های احراز هویت و هر داده‌ای است که برای بازیابی یا اصلاح آن نیاز به تماس با API است. ویژگی‌های کلیدی عبارتند از:

  • تأخیر: این داده‌ها محلی نیستند، بنابراین شامل درخواست‌های شبکه می‌شوند.
  • پنهان‌سازی (Caching): باید پنهان‌سازی شوند تا از بازیابی‌های غیرضروری جلوگیری شود.
  • همگام‌سازی: ممکن است نیاز به همگام‌سازی در چندین تب یا دستگاه داشته باشند.
  • مالکیت: منبع حقیقت واحد در سرور قرار دارد.

حالت کلاینت به داده‌هایی اشاره دارد که در مرورگر وجود دارند و لزوماً نیازی به تماس با API ندارند. این شامل حالت‌های رابط کاربری مانند شناسه تب فعال، دکمه‌های باز/بسته شدن مودال، مقادیر ورودی فرم و تنظیمات تم است. ویژگی‌های کلیدی عبارتند از:

  • تأخیر: این داده‌ها محلی هستند، بنابراین دسترسی به آن‌ها فوری است.
  • همگام‌سازی: نیازی به همگام‌سازی با سرور بک‌اند ندارند.
  • مالکیت: حالت گذرا است و به جلسه فعلی مرتبط است.

تله استفاده از حالت سراسری برای داده‌های سرور

یک ضد-الگوی رایج، به ویژه در میان توسعه‌دهندگانی که از Redux به راه‌حل‌های جدیدتر مهاجرت می‌کنند، این است که حالت سرور را مانند حالت کلاینت سراسری در نظر بگیرند. استفاده از ابزارهایی مانند Redux، Vuex یا Pinia برای پنهان‌سازی پاسخ‌های API اغلب منجر به کدهای تکراری می‌شود. شما در نهایت بای‌پلیت (Boilerplate) برای حالت‌های بارگذاری، مدیریت خطا و بی‌اعتبارسازی پنهان‌سازی می‌نویسید که قبلاً در کتابخانه‌های تخصصی وجود دارد.

برای مثال، یک کامپوننت را در نظر بگیرید که لیستی از کاربران را نمایش می‌دهد. اگر این لیست را در حالت کلاینت سراسری ذخیره کنید، باید چرخه عمر ناهمگام را به صورت دستی مدیریت کنید:

// ضد-الگو: مدیریت دستی حالت سرور در سمت کلاینت
useEffect(() => {
  const fetchUsers = async () => {
    setUsersLoading(true);
    try {
      const response = await fetch('/api/users');
      const data = await response.json();
      setUsers(data);
    } catch (error) {
      setUsersError(error);
    } finally {
      setUsersLoading(false);
    }
  };
  fetchUsers();
}, []);

این روش به خوبی مقیاس‌پذیر نیست. اگر کامپوننت دیگری به همان داده‌های کاربر نیاز داشته باشد، یا منطق را تکرار می‌کنید یا انتخاب‌گرهای پیچیده ایجاد می‌کنید که منجر به تزریق Prop و وابستگی شدید می‌شود.

راه‌حل: ابزارهای تخصصی برای حالت سرور

اکوسیستم مدرن کتابخانه‌های تخصصی را ارائه می‌دهد که به طور خاص برای مدیریت حالت سرور طراحی شده‌اند. در اکوسیستم React، React Query (TanStack Query) به استاندارد تبدیل شده است. در اکوسیستم Vue، Vue Query یا Orval همراه با APIهای ترکیبی بومی، نقش‌های مشابهی را ایفا می‌کنند.

این کتابخانه‌ها پنهان‌سازی، بازیابی مجدد در پس‌زمینه و حذف تکرار درخواست‌ها را به صورت پیش‌فرض مدیریت می‌کنند. در اینجا نحوه نمایش یک کامپوننت React با استفاده از React Query آمده است:

// بهترین شیوه: استفاده از React Query برای حالت سرور
import { useQuery } from '@tanstack/react-query';

function UserList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('/api/users').then(res => res.json())
  });

  if (isLoading) return <div>در حال بارگذاری...</div>;
  if (error) return <div>خطایی رخ داد: {error.message}</div>;

  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

این راه‌حل اعلانی (Declarative)، مختصر است و به طور خودکار پنهان‌سازی و همگام‌سازی را مدیریت می‌کند.

نتیجه‌گیری: ساده نگه دارید، محلی نگه دارید

قانون کلی ساده است: اگر داده از سرور می‌آید، از یک کتابخانه مدیریت حالت سرور استفاده کنید. اگر داده مربوط به رابط کاربری است و در مرورگر می‌ماند، از حالت کامپوننت محلی، Context یا یک فروشگاه کلاینت سراسری مانند Redux یا Pinia استفاده کنید. با رعایت مرزهای بین حالت سرور و حالت کلاینت، بای‌پلیت را کاهش می‌دهید، عملکرد را بهبود می‌بخشید و برنامه‌هایی می‌سازید که نگهداری آن‌ها آسان‌تر است. ابزار مناسب را برای کار مناسب انتخاب کنید و معماری شما از این بابت سپاسگزار خواهد بود.

Share: