مصطفی رحمتی
خواندن ۱۰ دقیقه·۳ ماه پیش

10 روش‌ مؤثر برای سازماندهی و طراحی بهتر برنامه‌های React

قبل از شروع، بر خود لازم می‌دانم به این نکته اشاره کنم که این مقاله ترجمه شده (همراه با بازنویسی و اقتباس) از مقاله‌ای دیگر است. بنابراین اگر خواندن مقاله تخصصی به زبان فارسی برای شما راحت نیست یا خواندن مقاله تخصصی به زبان انگلیسی برایتان جذاب‌تر است، می‌توانید به منبع اصلی این مقاله مراجعه کنید.



این تصویر با استفاده از هوش مصنوعی ایجاد شده است
این تصویر با استفاده از هوش مصنوعی ایجاد شده است

مقدمه

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

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

پایه‌های ناپایدار منجر به نتایج ناپایدار می‌شوند.


فهرست مطالب

  1. گروه‌بندی کامپوننت‌ها بر اساس مسئولیت‌های آن
  2. قرار دادن کامپوننت‌ها در فولدر جداگانه
  3. استفاده از مسیردهی مطلق (Absolute Paths)
  4. استفاده از common module ها
  5. از کتابخانه‌ها و ماژول‌های خارجی انتزاع ایجاد کنید
  6. مدیریت وابستگی‌ها بین ماژول‌ها/صفحات
  7. نگه داشتن اجزا در نزدیکی جایی که استفاده می‌شوند (اصل هم‌جواری رفتار یا LoB)
  8. دقت در استفاده از Utility Function ها
  9. استفاده هوشمندانه از Business Logic
  10. ثابت نگه داشتن وابستگی‌ها

1. گروه‌بندی کامپوننت‌ها بر اساس مسئولیت‌ آن‌ها

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

مهم است که فایل‌ها نه تنها بر اساس نقش‌های فنی؛ بلکه بر اساس مسئولیت‌های دامنه آن‌ها نیز ساختاربندی شده باشند.

از گروه‌بندی کامپوننت‌ها صرفاً بر اساس مسئولیت‌های فنی اجتناب کنید

/src │ ... │ └───components │ │ Header.js │ │ Footer.js │ │ ... └───containers │ │ InvoicesContainer.js │ │ PaymentProfilesContainer.js │ │ ... └───presenters │ │ InvoicesPresenter.js │ │ PaymentProfilesPresenter.js │ │ ... │ ...


بهتر است که کامپوننت‌ها را بر اساس مسئولیت‌های دامنه‌ای مانند مسیر صفحات (routes) یا ماژول‌ها (modules) گروه‌بندی شده باشند.

/src │ ... │ └───pages --> صفحاتی که هر کدام نمایانگر بخش‌های مختلف برنامه هستند │ └───billing │ │ └───invoices │ │ │ │ index.js │ │ │ │ ... │ │ └───payment-profiles │ │ │ │ index.js │ │ │ │ ... │ │ │ ... │ │ │ index.ts │ └───login │ │ index.js │ │ ... │ ...



2. قرار دادن کامپوننت‌ها در فولدر جداگانه

برای کامپوننت‌های پیچیده، بهتر است آن‌ها را در فولدرهای جداگانه سازماندهی و ایجاد کنیم تا بتوانیم ساب‌کامپوننت‌های (SubComponents) آن‌ها را لیست کنیم.

⛔ از داشتن یک فایل برای هر کامپوننت خودداری کنید.

/src │ └───components │ │ Accordion.ts | | Alert.ts │ │ ... │ └───...


✅ ترجیحاً برای هر کامپوننت یک دایرکتوری جداگانه ایجاد کنید.

/src │ └───components │ │ ... │ └───accordion │ │ │ index.ts │ │ │ ... │ └───alert │ │ index.ts │ │ types.ts │ │ Alert.tsx │ │ AlertTitle.tsx │ │ Alert.stories.tsx │ │ Alert.test.tsx │ │ ... │ └───...


مثال برای داشتن دایرکتوری جداگانه برای هر کامپوننت
مثال برای داشتن دایرکتوری جداگانه برای هر کامپوننت



3. استفاده از مسیردهی مطلق (Absolute Paths)

استفاده از نوع مناسب مسیردهی در پروژه می‌تواند پیمایش و نگهداری کد را آسان‌تر کند، به‌ویژه هنگامی که پروژه گسترش پیدا می‌کند. همچنین ری‌فکتور (refactor) پروژه را بسیار راحت‌تر خواهد کرد.

⛔ از مسیردهی نسبی (Relative Paths) که در پروژه‌های بزرگ، مدیریت را دشوار و همواره مستعد خطا هستند، خودداری کنید.

import { formatDate } from '../../../utils';


✅ ترجیحاً از مسیرهای مطلق (Absolute Paths) که خوانایی کد را بهبود می‌بخشند و بازنگری (refactor) کد را آسان‌تر می‌کنند، استفاده کنید.

import { formatDate } from '@common/utils';



4. استفاده از common module ها

ماژول‌های مشترک (Common module) نقش بسیار مهم و حیاتی در جلوگیری از تکرار کد و افزایش قابلیت استفاده مجدد از کد را در سراسر برنامه، ایفا می‌کنند.

می‌توانید متدهای کمکی (utility method)، ثابت‌ها (constants)، کامپوننت‌ها، محاسبات (calculations) و غیره را در این ماژول مشترک نگهداری کنید.

این متمرکز سازی به مدیریت بهتر کد و استفاده مجدد از آن کمک می‌کند.

⛔ از پراکنده کردن ابزارها و کامپوننت‌های مشترک در مکان‌های مختلف پروژه خودداری کنید.


✅ ترجیحاً یک ماژول مشترک اختصاصی برای تمام کامپوننت‌ها و ابزارهای عمومی که در صفحات یا ماژول‌های مختلف استفاده می‌شوند، داشته باشید.

/src │ ... │ └───common │ └───components │ │ └───dialogs │ │ │ │ index.js │ │ └───forms │ │ │ │ index.js │ │ │ ... │ └───hooks │ │ │ useDialog.js │ │ │ useForm.js │ │ │ ... │ └───utils │ │ │ ... └───pages │ └───billing │ │ └───invoices │ │ │ │ index.js │ │ │ │ ... │ ...



دیدگاه مترجم:

به دو علت استفاده از این ساختار بندی در پروژه‌های فرانت مخالفم:

  1. فولدرهایی که در سطح root یا src پروژه قراردارند، (جزء در موارد خاص مانند pages یا features) به طور معمول بیانگر موارد مشترک در کل پروژه هستند و نیازی به جداسازی این موارد در زیر دایرکتوری‌ای تحت عنوان common یا shared نمی‌باشد (مگر اینکه علت استفاده از این ساختار دلیل قانع‌کننده‌ی دیگری داشته باشد).
  2. این ساختار باعث ایجاد پیمایش‌های تودرتو می‌شود که نگهداری و دسترسی‌پذیری کد را کاهش می‌دهد.


توجه داشته باشید که با استفاده از فولدر common یا shared در سطح root یا src پروژه مخالف هستم، اما ممکن هست که هر دایرکتوری (غالباً در فولدر features) بنا به نیاز خود دارای یک دایرکتوری common یا shared نیز باشد.



5. از کتابخانه‌ها و ماژول‌های خارجی انتزاع ایجاد کنید

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

استفاده مستقیم از کتابخانه‌ها یا کامپوننت‌های 3rd party در پروژه می‌تواند منجر به ایجاد مشکلاتی شود؛ اگر API‌های خارجی تغییر کنند یا بخواهید کامپوننت‌ها یا کتابخانه‌ها را با گزینه دیگری جایگزین کنید، باید در تمام مکان‌هایی که از آن‌ها استفاده شده، تغییرات لازم اعمال شوند، به جای آنکه فقط در یک مکان خاص این به‌روزرسانی انجام گردد.

محصور کردن (Wrapp) کتابخانه یا ماژول شخص ثالث در یک کامپوننت سفارشی دیگر به ما این امکان را می‌دهد که یک API یکنواخت و یکپارچه در درون برنامه‌ی خود حفظ کنیم و در صورت نیاز در آینده، جایگزین کردن ماژول را با ماژول دیگری آسان‌تر می‌کند.

⛔ از استفاده مستقیم از کامپوننت‌ها یا کتابخانه‌های 3rd party در پروژه خود خودداری کنید.

// XYZ_Component.ts (file 1) import { Button } from 'react-bootstrap'; // ABC_Component.ts (file 2) import { Button } from 'react-bootstrap';


✅ ترجیحاً کتابخانه‌ها یا کامپوننت‌های 3rd party را در یک کامپوننت سفارشی دیگر محصور و بسته‌بندی (Wrapp) کنید.

// XYZ_Component.ts (file 1) import { Button } from '@components/ui'; // ABC_Component.ts (file 2) import { Button } from '@components/ui';



6. مدیریت وابستگی‌ها بین ماژول‌ها/صفحات

مدیریت هوشمندانه وابستگی‌ها با متمرکزسازی منابع مشترک در یک ماژول مشترک (shared common)، می‌تواند به‌طور قابل‌توجهی قابلیت نگهداری و استفاده مجدد از کد را افزایش دهد.

اگر چیزی بیش از یک‌بار در دو یا چند صفحه یا ماژول دیگر استفاده می‌شود، بهتر است آن را به یک ماژول مشترک انتقال دهید.

ذخیره‌سازی کامپوننت‌ها یا ابزارهای مشترک در یک ماژول مشترک، نیاز به تکرار کد در بخش‌های مختلف برنامه را از بین می‌برد، که باعث می‌شود کدبیس سبک‌تر و نگهداری آن آسان‌تر شود.

همچنین این کار وابستگی‌ها را برای هر ماژول و هر صفحه به‌وضوح مشخص خواهد کرد.



7. نگه داشتن اجزا در نزدیکی جایی که استفاده می‌شوند (اصل هم‌جواری رفتار یا LoB)

هرچه برای توسعه‌دهنده پیدا کردن یک قطعه کد آسان‌تر و سریع‌تر باشد، بهتر است.

اصل هم‌جواری رفتار (Locality of Behavior - LoB) پیشنهاد می‌کند که کدبیس به‌گونه‌ای سازمان‌دهی شود که کامپوننت‌ها، توابع و منابع در نزدیکی محل مورد استفاده‌شان در برنامه قرار گیرند. این استراتژی، معماری modular را ترویج می‌کند که در آن هر بخش از سیستم خودکفا است.

این رویکرد خوانایی و نگهداری‌پذیری کد را بهبود می‌بخشد. زمانی که توسعه‌دهندگان روی یک ویژگی (feature) کار می‌کنند، تمام کدهای مرتبط در نزدیکی یکدیگر قرار دارند که فهم و تغییر آن‌ها را آسان‌تر می‌کند. همچنین بار شناختی ردیابی از طریق فایل‌ها و ماژول‌های دور (خارج از دید در یک نگاه) را کاهش می‌دهد.

/src │ ... │ └───common │ └───components │ │ │ ... │ └───hooks │ │ │ ... │ └───utils │ │ │ ... └───pages │ └───billing │ │ └───invoices │ │ │ │ index.js │ │ │ │ ... │ │ └───payment-profiles │ │ │ │ index.js │ │ │ │ ... │ │ └───hooks │ │ │ │ index.js │ │ │ │ useInvoices.js │ │ │ │ usePaymentProfiles.js │ │ │ │ ... │ │ └───utils │ │ │ │ index.js │ │ │ │ formatAmount.js │ │ │ │ ... │ ...


اصل محلی‌سازی/هم‌جواری رفتار (LoB) بر اهمیت قرار دادن اجزا به صورت منطقی در همان بخش‌هایی از کد تأکید دارد که آن‌ها استفاده می‌شوند. به عنوان مثال، اگر یک کامپوننت خاص تنها در یک صفحه خاص استفاده می‌شود، بهتر است آن کامپوننت در همان دایرکتوریِ آن صفحه قرار گیرد تا دسترسی و مدیریت آن آسان‌تر شود.



8. دقت در استفاده از Utility Function ها

توابع کمکی (Utility function) معمولاً بخش‌های قابل استفاده‌ی مجددِ کدی هستند که به قوانین کسب‌وکار یا منطق برنامه وابسته نیستند. آن‌ها بیشتر به ارائه کمک‌های عمومی در سراسر سیستم می‌پردازند (مانند فرمت تاریخ‌ها، تبدیل نوع داده‌ها و غیره).

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

همچنین، منطق تجاری و قوانین کسب‌وکار معمولاً بیشتر از توابع کمکی تغییر می‌کنند، بنابراین با جدا کردن آن‌ها، نگهداری کلی کد بهبود می‌یابد.

⛔ از افزودن منطق کسب‌وکار به Utility function ها خودداری کنید.


✅ ترجیحاً منطق کسب‌وکار را به توابع جداگانه منتقل کنید.



9. استفاده هوشمندانه از Business Logic

ادغام مستقیم منطق کسب‌وکار در کامپوننت‌های رابط کاربری (UI) می‌تواند منجر به ایجاد مشکلاتی مانند سخت‌تر شدن تستِ کد و جداسازی نادرست مسئولیت‌ها شود. این امر همچنین می‌تواند منجر به ایجاد کامپوننت‌های حجیم شود که مدیریت و به‌روزرسانی آن‌ها را دشوار می‌کند.

در React، هوک‌های سفارشی ابزار بسیار خوبی برای انتزاع و جداسازی منطق کسب‌وکار از کامپوننت‌ها هستند. با استفاده از هوک‌ها، می‌توانید منطق کسب‌وکار را در یک مکان جداگانه کپسوله کنید و رابط کاربری خود را تمیز و متمرکز بر رندرینگ نگه دارید.

این جداسازی نه تنها کامپوننت‌های ما را ماژولارتر (modular) و مدیریت آن‌ها را آسان‌تر می‌کند، بلکه قابلیت استفاده مجدد و نگهداری را نیز افزایش می‌دهد

⛔ از ترکیب منطق کسب‌وکار با رابط کاربری خودداری کنید.


✅ ترجیحاً منطق کسب‌وکار را از رابط کاربری جدا کنید. از هوک‌های سفارشی استفاده کنید.



10. ثابت نگه داشتن وابستگی‌ها

در مدیریت پروژه‌های جاوااسکریپت، فایل package.json نقش حیاتی و مهمی را ایفا می‌کند. این فایل وابستگی‌های پروژه را تعریف می‌کند؛ یعنی پکیج‌های خارجی‌ای که پروژه برای عملکرد صحیح خود به آن‌ها نیاز دارد. ثابت نگه داشتن این وابستگی‌ها به معنای مشخص کردن نسخه‌های دقیق این پکیج‌ها به‌جای استفاده از بازه‌های نسخه است.
با ثابت نگه داشتن وابستگی‌ها، اطمینان حاصل می‌کنید که همه افرادی که روی پروژه کار می‌کنند، همانند نسخه production، از همان نسخه دقیق هر پکیج استفاده می‌کنند و تناقضاتی که ممکن است به دلیل به‌روزرسانی‌های جزئی (minor update) یا پچ‌ها (patch) رخ دهد، از بین می‌رود.

⛔ از استفاده از بازه‌های نسخه (version ranges) در فایل package.json خودداری کنید.

{ &quotdependencies&quot: { &quotexpress&quot: &quot^4.17.1&quot, &quotreact&quot: &quot>=16.8.0&quot } }


✅ ترجیحاً از نسخه‌های دقیق در فایل package.json خود استفاده کنید.

{ &quotdependencies&quot: { &quotexpress&quot: &quot4.17.1&quot, &quotreact&quot: &quot16.8.0&quot } }



  1. 10 ways to better organize and design your React Application


FrontEnd Developer (Javascript/ReactJS/NextJS)
شاید از این پست‌ها خوشتان بیاید