قضیه از این جا شروع میشه که شما یه روز به عنوان یه فرانتاند دولوپر (یا فول استک دولوپر) از خواب بیدار میشید، سیستمتون رو روشن میکنید و مثل همیشه توی ترمینال npx create-next-app@latest رو وارد میکنید... و بعد بدون فکر کردن زیاد شروع میکنید به یکی یکی انتخاب کردن گزینههایی که cli بهتون ارائه میده.
اما این بار با یه سوال جدید روبرو میشید. ازتون پرسیده میشه که آیا میخواید از App Router استفاده کنید. شما هنوز نمیدونید این فیچر جدیده نکسته یا چی! ولی از اونجایی که کنارش نوشته (Recommended) بله رو انتخاب میکنید. ولی وقتی پروژهتون آماده میشه ساختار فولدرها اون چیزی نیست انتظارش رو داشتید و این شما رو گیج میکنه. حالا به جای فولدر pages و فایلهای index.jsx/tsx و _app.jsx/tsx داخلش یه فولدر جدید به اسم app ظاهر شده با زیر مجموعه page.jsx./tsx و layout.jsx/tsx. اینجاست که شما پروژهتون رو میبندید و میرید سراغ داکیومنتهای Next JS (که حالا به طور مشخص برای کار کردن با App Router به روز شده) تا احتمالاً یه نصف روز رو توش بچرخین و متوجه بشین داستان از چه قراره و این تغییرات جدید چی هستند.
اینجا من میخوام کوتاه در مورد تجربه خودم در کار کردن با این فیچر جدید NextJS صحبت کنم و از تفاوتهای قابل لمسش با سیستم روتینگ قبلی این فریمورک یعنی Page Router بگم. طبیعتاً پیشفرض من اینه که شما به عنوان خواننده با NextJS حداقل یه آشنایی اولیه دارید.
App Router چیه؟
اگر ترندهای دنیای برنامهنویسی و به خصوص توسعه وب رو توی چند سال اخیر دنبال کرده باشید میدونید که فریمورکها در این حوزه دارن به سمتی میرن که بتونن کنترل بیشتری به برنامهنویسها و توسعهدهندههای این حوزه روی ماژولهای مختلف پروژهشون بدن. چه از لحاظ کنترل روی UI و چه مواردی مثل State Management و غیره. برای همینه که هنوز هم هفت سال بعد از معرفی، تب و تاب معماری میکرو فرانتاند داغه.
معرفی App Router در NextJS هم یه قدم دیگه است در همین راستا.
App Router درست مثل Page Router از فایل سیستم برای تعیین مسیرها توی اپلیکیشن ما استفاده میکنه. ولی مهمترین تفاوتش اینه که حالا به ما توی رفتار صفحات مختلف در زمینه layout ها و گروه بندی مسیرها کنترل بیشتری میده. بذارید بیشتر توضیح بدم.
شما وقتی یه پروژه boilerplate میسازید داخل فولدر app دو فایل page.jsx و layout.jsx رو دارید. داخل page.jsx صفحه اصلیتون رو به همراه کامپوننتهایی که میخواید داخلش رندر بشه میسازید و داخل layout.jsx پیکربندی و آرایش کلی صفحه و کامپوننتهایی مثل Navbar یا Footer رو میگذارید. اما حالا فرض کنید صفحهای دارید مثل settings یا signin/signup که از لیاوت کلی اپلیکیشنتون پیروی نمیکنه.
توی نسخههای قبلی NextJS شما برای حل این مشکل باید داخل فولدر pages ساختار اپلیکیشن رو ایجاد میکردید و بعد کامپوننتهایی که میخواستید رو برای اساس لیاوت توی هر صفحه کپی میکردید.
اما حالا توی App Router میتونید با ساختن یه فولدر جدید توی خود فولدر app و ایجاد دو فایل page.jsx و layout.jsx جدید یک لیاوت مجزا از صفحات اصلیتون بسازید. حتی میتونید این لیاوتها رو داخل هم جاسازی کنید (nested layouts). یعنی برای مثال میتونید یک صفحهبندی کلی داشته باشید برای قسمت settings اپلیکیشن و بعد داخل اون دو لیاوت جدید با ساختن صفحات admin-settings و user-settings ایجاد کنید که هم کامپوننتهای لیاوت settings رو براتون رندر کنن و هم کامپوننتهای اختصاصی خودشون رو.
علاوه بر این شما میتونید با ایجاد فایل error.jsx و loading.jsx برای هر مسیر بخصوص در اپلیکیشن یک صفحه خطا و بارگذاری متفاوت داشته باشید. در زیر میتونید مثالی از ساختار فولدرها و فایلها در یک پروژه وبلاگ ببینید.
. └── app/ ├── page.tsx ├── about/ │ ├── page.tsx │ └── layout.tsx└── articles/ └── article/ └── [id]/ ├── page.tsx ├── layout.tsx └── components/ ├── BlogArticle.tsx └── Author.tsx
البته این رو باید اضافه کنم که مبحث Routing در Next JS همینجا تموم نمیشه و اگر قصد کار کردن با App Router رو دارید، حتماً سری به داکیومنتهای این فیچر بزنید.
Data Fetching – همه جا سرور شماست
یکی از ویژگیهای NextJS که در زمان معرفی خیلی ازش استقبال شد این بود که حالا شما اجازه داشتید نه تنها روی رابط کاربری بلکه روی کدی در سمت سِرور هم اجرا میشه کار کنید. حالا شما میتونستید با توابعی مثل getStaticProps و getServerSideProps اطلاعاتتون رو با سرعت و کنترل بیشتری از سرورتون بگیرید و مستقیم پاس بدید به صفحات و کامپوننتهایی که بهش نیاز داشتند.
خبر بد اینه که توی App Router دیگه این توابع کار نمیکنند.
در عوض خبر خوب اینه که در نسخه 13.4 نه تنها مفاهیمی مثل SSR ، SSG و ISR منسوخ نشدن بلکه کار کردن با سرور خیلی راحتتر شده. در واقع حالا با معرفی React Server Components توی NextJS همه کامپوننتهای شما روی سرور ساخته میشن در زمان build به کاربر ارائه میشن. امتحان کنید. همین الان npm run dev رو اجرا کنید و بعد توی اپلیکیشنتون یه console.log بگیرید و کنسول مرورگرتون رو چک کنید. میبینید که اونجا هیچ چیزی ظاهر نشده؛ اما در عوض حالا لاگ رو دارید توی کنسول سرور (یعنی همون Node که توی ترمینال داره به شما نشون داده میشه) میبینید. البته میتونید با use-client directive یک کامپوننت ( و همه کامپوننتهای زیر مجموعهاش) رو تبدیل به client component بکنید.
اما برای data fetching حالا NextJS داره راه حل جدیدی میده که در واقع چندان جدید هم نیست.
برای این کار NextJS اومده Fetch API که خود جاوا اسکریپت ارائه میداد رو گسترش داده و برای گرفتن دیتا از سرور اون رو در اختیار ما گذاشته.
بیاید با یه مثال این موضوع رو بررسی کنیم. فرض کنید شما میخواید اطلاعاتتون رو از endpoint زیر بگیرید:
https://www.example.com/api/data.json
قبلاً شما خارج از کامپوننت اطلاعات رو از سرور میگرفتید و بعد بوسیله props اونها رو به سمت کاربر میفرستادید. به این شکل:
export default function MyComponent({data}) { //Component Details } export async function getStaticProps() { const result = await fetch("https://www.example.com/api/data.json"); const data = result.json(); return {props:data} }
اما حالا برای گرفتن دیتا مستقیماً از داخل خود کامپوننت عمل میکنیم:
export default async function MyComponent() { const result = await fetch("https://www.example.com/api/data.json"); const data = result.json(); }
یکی دیگه از ترندهایی که در سالهای اخیر توی فریمورکها و کتابخونههای جاوااسکریپت میبینیم حذف کردن توابع اضافه و پیادهسازی روشهای اجرایی روی متدهای built-in هست. اینطوری علاوه بر بالا بردن سرعت عملکرد اپلیکیشن، کار برای کسانی که میخوان این فریمورکها رو توی پروژههای از قبل تعریفشدهشون استفاده کنند، راحتتر میشه.
هدف توسعهدهندگان NextJS از به کار بردن Fetch API هم همین بوده.
اما حالا اگه بخوایم این اطلاعات رو از فایلهای لوکال بگیریم چی؟
کاری نداره... شما کافیه به تابعی که قراره فایلهای سیستمت رو بخونه بگی اونها رو به شکل Promise بهت برگردونه. اون وقت میتونی از قابلیت async/await توی کامپوننت React استفاده کنی. به شکل زیر:
export function getAllData(){ return new Promise((resolve , reject)=>{ try { //Code for reading you data resolve(allPosts) } catch(error) { reject(error) } }) }
حرف آخر
آیا استفاده از App Router پیشنهاد میشه؟ قطعاً جواب این سوال بستگی داره به کد بیسی که شما دارید ازش استفاده میکنید، داره. چون با این که Vercel این فیچر رو شیپ شده و در مرحله قابل اجرا در نظر گرفته ، طبق تجربه من هنوز با این که App Router رو در تطابق کامل با اکوسیستم React/NextJS ببینیم فاصله زیادی وجود داره. اما اگر شما هم یک توسعهدهنده وب هستید که در طول روز با NextJS سر و کار دارید، پیشنهاد میکنم که حتماً از الان کار با App Router رو شروع کنید. چون همونطور که اشاره کردم با توجه به حرکت دنیای وب به سمت ابزارهای توسعه ساده و در دسترسT روزی که این روش کاملاً جایگزین Page Router بشه دور نیست.
