شیوه توسعه و دیباگ اپلیکیشن در Next.js — آموزش Next.js (بخش دوم)
در بخش قبلی این سری مقالات آموزش فریمورک Next.js با مفاهیم مقدماتی آن آشنا شدیم و شیوه مشاهده کد منبع را مورد بررسی قرار دادیم. در این بخش در مورد شیوه توسعه و دیباگ اپلیکیشن در Next.js صحبت میکنیم. برای مطالعه بخش قبلی این مجموعه مقالات آموزشی روی لینک زیر کلیک کنید:
Bundle-های اپلیکیشن
زمانی که سورس صفحه را بررسی میکنیم، با دستهای از فایلهای جاوا اسکریپت مواجه میشویم که بارگذاری شدهاند:
کار خود را با قرار دادن کد در ابزار قالببندی HTML به نام HTML formatter (+) آغاز میکنیم تا خوانایی و درک آن افزایش یابد:
1<!DOCTYPE html>
2<html>
3
4<head>
5 <meta charSet="utf-8" />
6 <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
7 <meta name="next-head-count" content="2" />
8 <link rel="preload" href="/_next/static/development/pages/index.js?ts=1572863116051" as="script" />
9 <link rel="preload" href="/_next/static/development/pages/_app.js?ts=1572863116051" as="script" />
10 <link rel="preload" href="/_next/static/runtime/webpack.js?ts=1572863116051" as="script" />
11 <link rel="preload" href="/_next/static/runtime/main.js?ts=1572863116051" as="script" />
12</head>
13
14<body>
15 <div id="__next">
16 <div>
17 <h1>Home page</h1></div>
18 </div>
19 <script src="/_next/static/development/dll/dll_01ec57fc9b90d43b98a8.js?ts=1572863116051"></script>
20 <script id="__NEXT_DATA__" type="application/json">{"dataManager":"[]","props":{"pageProps":{}},"page":"/","query":{},"buildId":"development","nextExport":true,"autoExport":true}</script>
21 <script async="" data-next-page="/" src="/_next/static/development/pages/index.js?ts=1572863116051"></script>
22 <script async="" data-next-page="/_app" src="/_next/static/development/pages/_app.js?ts=1572863116051"></script>
23 <script src="/_next/static/runtime/webpack.js?ts=1572863116051" async=""></script>
24 <script src="/_next/static/runtime/main.js?ts=1572863116051" async=""></script>
25</body>
26
27</html>
چنان که میبینید 4 فایل جاوا اسکریپت اعلان شدهاند که در بخش head با استفاده از دستور زیر «پیشبارگذاری» (Preload) میشوند:
1rel="preload" as="script"
فهرست این فایلها به صورت زیر است:
- /_next/static/development/pages/index.js (96 LOC)
- /_next/static/development/pages/_app.js (5900 LOC)
- /_next/static/runtime/webpack.js (939 LOC)
- /_next/static/runtime/main.js (12k LOC)
این لیست به مرورگر اعلام میکند که در اولین فرصت ممکن و پیش از آغاز گردش رندرینگ نرمال صفحه، شروع به بارگذاری این فایلها بکند. بدون وجود این دستورها، اسکریپتها با یک تأخیر اضافی بارگذاری میشوند، از این رو این دستورها موجب بهبود عملکرد بارگذاری صفحه میشوند.
سپس این 4 فایل در انتهای body باه همراه فایل زیر بارگذاری میشوند:
/_next/static/development/dll/dll_01ec57fc9b90d43b98a8.js (31k LOC)
یک قطعه کد JSON نیز برخی مقادیر پیشفرض برای دادههای صفحه تعیین میکند:
1<script id="__NEXT_DATA__" type="application/json">
2{
3 "dataManager": "[]",
4 "props": {
5 "pageProps": {}
6 },
7 "page": "/",
8 "query": {},
9 "buildId": "development",
10 "nextExport": true,
11 "autoExport": true
12}
13</script>
4 فایل bundle که بارگذاری شدهاند، یک قابلیت به نام «افراز کد» (Code Splitting) پیادهسازی میکنند. فایل index.js کد مورد نیاز برای کامپوننت index را ارائه میکند که مسیر / را عرضه میکند. اگر صفحههای بیشتری داشته باشیم، bundle-های بیشتری برای هر صفحه خواهیم داشت که در صورت نیاز در ادامه بارگذاری خواهند شد تا زمان بارگذاری صفحه عملکرد بهینهتری داشته باشد.
آیکون پایین-راست به چه معنی است؟
آیا آن آیکون کوچک در سمت راست-پایین صفحه را که مانند یک رعدوبرق است مشاهده کردید؟
اگر ماوس را روی آن ببرید، عبارت «Prerendered Page» را میبینید:
این آیکون تنها در مرحله توسعه دیده میشود و اعلام میکند که صفحه حائز شرایط لازم برای بهینهسازی استاتیک خودکار است. این گفته به آن معنا است که این صفحه به دادههایی که در زمان فراخوانی واکشی میشوند وابستگی ندارد و میتواند از پیش رندر شده و به صورت یک فایل HTML استاتیک در زمان build (یعنی با اجرای دستور npm run build) ساخته شود.
Next این مسئله را از طریق عدم حضور متد ()getInitialProps که به کامپوننت صفحه الصاق میشود تشخیص میدهد. در این حالت، صفحه ما سریعتر میشود، زیرا به جای حرکت از مسیر سرور Node.js که خروجی HTML تولید میکند، به صورت استاتیک به صورت یک فایل HTML عرضه میشود. آیکون مفید دیگری که ممکن است در کنار آن یا به جای آن روی صفحههای بدون پیش رندر ظاهر شود، یک مثلث انیمیت شده کوچک است:
این نشانگر کامپایل است و زمانی ظاهر میشود که یک صفحه را ذخیره کنید و Next.js اپلیکیشن را پیش از اجرای بارگذاری مجدد کد در اپلیکیشن به صورت خودکار کامپایل کند. بدین ترتیب به روشی مناسب درمییابیم که آیا اپلیکیشن هم اینک کامپایل شده است یا نه و میتوانیم بخشی را که روی آن کارمیکنیم تست نماییم.
نصب ابزارهای توسعهدهنده ریاکت
Next.js بر اساس React طراحی شده است و از این رو یکی از ابزارهای بسیار مفید که قطعاً باید نصب کنیم، «ابزارهای توسعهدهنده ریاکت» (React Developer Tools) است. این ابزارها برای هر دو مرورگر کروم (+) و فایرفاکس (+) عرضه شده است و ابزاری ضروری برای بازرسی اپلیکیشنهای ریاکت محسوب میشود.
توجه کنید که ابزارهای توسعهدهنده ریاکت اختصاصی به Next.js ندارد، اما در این بخش آنها را معرفی میکنیم چون شاید با 100% امکاناتی که این ابزارها ارائه میکنند آشنا نباشید. در هر حال بررسی اندک ابزارهای دیباگ، ضرری نخواهد داشت. ابزارهای توسعهدهنده ریاکت یک بخش inspector دارند که درخت کامپوننتهای ریاکت را نمایش میدهد. این درخت صفحه وب را میسازد و در مورد هر کامپوننت میتوان props، حالت، قلابها و مواردی از این دست را مورد بررسی قرار داد. زمانی که ابزارهای توسعهدهنده ریاکت را نصب کردید، میتوانید devtools معمولی مرورگر را باز کنید (کلید F12) تا با دو پنل جدید به نامهای Components و Profiler مواجه شوید.
اگر ماوس را روی کامپوننتها ببرید، میبینید که مرورگر در صفحه، بخشهایی را که از سوی کامپوننت رندر میشوند انتخاب میکند. اگر هر کدام از کامپوننتها را در درخت انتخاب کنید، پنل راست ارجاعی به کامپوننت والد نمایش میدهد و props ارسالی به آن دیده میشود:
همچنین میتوانید به سادگی با کلیک روی نام کامپوننتها به آنها بروید. همچنین میتوانید روی آیکون چشم در نوار ابزار Developer Tools کلیک کنید تا عنصر DOM را بازرسی کنید و اگر از آیکون اول که شکل ماوس را دارد استفاده کنید، میتوانید با بردن ماوس روی یک عنصر UI مرورگر مستقیماً کامپوننت ریاکت که آن را رندر میکند، ببینید. از آیکون «حشره» (Bug) میتوانید برای لاگ کردن دادههای یک کامپوننت در کنسول مرورگر استفاده کنید.
این امکان بسیار جالبی است، زیرا زمانی که دادهها را به صورت پرینت شده در اختیار داشته باشید، میتوانید روی هر عنصر راست-کلیک کنید و دکمه Store as a global variable را بزنید. برای نمونه ما این کار را در مورد prop با نام url انجام دادیم و اینک میتوانیم آن را در کنسول با استفاده از متغیر موقتی که به آن اختصاص یافته یعنی temp1 بازرسی کنیم:
با استفاده از قابلیت «نگاشتهای منبع» (Source Maps) که به صورت خودکار در Next.js در حالت توسعه بارگذاری میشوند، در پنل Components روی <> کلیک میکنیم و DevTools به پنل Source سوئیچ کرده و سورس کد کامپوننت را به ما نمایش میدهد.
زبانه Profiler بسیار جالبتر است. این زبانه امکان ثبت سوابق یک تعامل و آن چه در اپلیکیشن رخ میدهد را فراهم میسازد. ما در اپلیکیشن خود در حال حاضر نمیتوانیم آن را نشان دهیم چون دست کم به 2 کامپوننت نیاز داریم. اما در بخشهای بعدی این سری مقالات در مورد آن بیشتر صحبت خواهیم کرد.
همه تصاویر فوق در مرورگر کروم نمایش یافتهاند، اما ابزارهای توسعهدهنده ریاکت در فایرفاکس نیز عملکرد مشابهی دارند.
تکنیکهای دیگر دیباگ
علاوه بر ابزارهای توسعهدهنده ریاکت که برای ساخت اپلیکیشنهای Next.js ضروری هستند، روشهای دیگری نیز برای دیباگ این اپلیکیشنها وجود دارند. در ادامه دو مورد از این روشها را بررسی میکنیم. روش نخست به صورت بدیهی استفاده از Next.js و دیگر ابزارهای API کنسول است. در این روش هنگامی که Next را با استفاده از دستور npm run dev آغاز کنید، اپلیکیشنهای Next یک گزاره لاگ را در کنسول مرورگر و یا در ترمینال پرینت میکنند.
به طور خاص در صورتی که صفحه از سرور بارگذاری میشود، زمانی که URL به آن اشاره کند یا دکمه رفرش را بزنید، هرگونه لاگ کنسول در ترمینال اجرا میشود. انتقالهای بعدی صفحه که با کلیک ماوس رخ میدهند موجب میشود که لاگ شدن در کنسول و درون مرورگر اجرا شود. نکته فوق در مواردی که در مورد غیب شدن لاگها حیرتزده شوید بسیار کارگشا خواهد بود. ابزاری دیگری که برای دیباگ ضروری است، گزاره debugger است. افزودن این گزاره به یک کامپوننت موجب میشود که مرورگر، رندر کردن صفحه را متوقف کند:
این ابزار بسیار جالب است، زیرا میتوانید از دیباگر مرورگر برای بررسی یک مقدار و اجرای اپلیکیشن به صورت خط به خط استفاده کنید. همچنین میتوانید از ابزار دیباگر VS Code برای دیباگ کردن کدهای سمت سرور استفاده کنید.
افزودن صفحه دوم به سایت
اکنون که درکی مناسب از ابزارهای مورد نیاز برای توسعه اپلیکیشنهای Next.js داریم، میتوانیم کار توسعه نخستین اپلیکیشن خود را، که در بخش قبلی این سری مقالات آغاز کردیم، از جایی که باقی مانده بود، از سر بگیریم:
ما میخواهیم یک صفحه دوم به وبسایت خود که یک بلاگ است اضافه کنیم. این صفحه در مسیر /blog عرضه میشود و در حال حاضر صرفاً شامل یک صفحه استاتیک است که مشابه وضعیت کامپوننت نخست ما یعنی index.js است.
پس از ذخیره فایل جدید با اجرای دستور npm run dev که از قبل اجرا میشود، میتواند صفحه جدید را بدون نیاز به راهاندازی مجدد، عرضه کند. زمانی که به آدرس http://localhost:3000/blog برویم، این صفحه جدید را میبینیم:
خروجی ترمینال نیز به صورت زیر است:
باید به این واقعیت توجه داشته باشیم که /blog به نام فایل و موقعیت آن در پوشه pages وابسته است. برای نمونه میتوانید یک صفحه به صورت pages/hey/ho ایجاد کنید که در آدرس http://localhost:3000/hey/ho نمایش مییابد. از نظر URL، نام کامپوننت درون فایل اهمیتی ندارد.
اینک تلاش میکنیم سورس صفحه را مشاهده کنیم. زمانی که صفحه از سرور بارگذاری میشود، next/static/development/pages/blog.js_/ را به عنوان یکی از bundle-های بارگذاریشده لیست خواهد کرد. این وضعیت برخلاف صفحه اصلی وبسایت است که next/static/development/pages/index.js_/ را ارائه میکرد. دلیل این امر، امکان خودکار افراز کد است که به bundle-ی که صفحه اصلی را عرضه میکند نیازی ندارد. به این ترتیب تنها bundle-ی که صفحه بلاگ را عرضه میکند بارگذاری میشود.
همچنین میتوانیم یک تابع بینام از blog.js اکسپورت کنیم:
1export default () => (
2 <div>
3 <h1>Blog</h1>
4 </div>
5)
یا در صورت ترجیح، از ساختار تابع غیر arrow استفاده کنیم:
1export default function() {
2 return (
3 <div>
4 <h1>Blog</h1>
5 </div>
6 )
7}
لینک کردن دو صفحه
اکنون که دو صفحه داریم که از سوی index.js و blog.js تعریف شدهاند، میتوانیم لینکها را معرفی کنیم. لینکهای معمولی HTML درون صفحهها با استفاده از تگ a اجرا میشوند:
1<a href="/blog">Blog</a>
این کار در Next.js امکانپذیر نیست، البته این کار از نظر فنی ممکن است، چون ما روی وب هستیم و روی وب هیچچیزی از کار نمیافتد، اما یکی از مزیتهای اصلی Next.js این است که وقتی صفحه بارگذاری شد، گذار به صفحه دیگر به لطف رندرینگ سمت کلاینت بسیار سریع است. بدین ترتیب اگر از یک لینک a ساده مانند زیر استفاده کنید:
1const Index = () => (
2 <div>
3 <h1>Home page</h1>
4 <a href='/blog'>Blog</a>
5 </div>
6)
7
8export default Index
وقتی DevTools را باز کنید و به پنل Network بروید، میبینید که وقتی نخستین بار http://localhost:3000/ را بارگذاری کردیم، همه bundle-های صفحه بارگذاری شدهاند:
اگر اکنون روی دکمه Preserve log کلیک کنیم تا از پاک شدن پنل Network جلوگیری شود و سپس روی لینک Blog کلیک نماییم، اتفاقی به صورت زیر رخ میدهد:
بدین ترتیب همه کدهای جاوا اسکریپت بار دیگر از سرور بارگذاری میشوند، اما بدیهی است که وقتی این کدها را در اختیار داریم، قصد نداریم آنها را مجدداً بارگذاری کنیم، ما صرفاً به bundle صفحه blog.js نیاز داریم که تنها بخش مورد نیاز برای بارگذاری صفحه جدید است. برای حل این مشکل از یک کامپوننت ارائه شده از سوی Next به نام Link استفاده میکنیم. آن را به روش زیر ایمپورت میکنیم:
1import Link from 'next/link'
سپس از آن استفاده کرده و لینک خود را به صورت زیر درون آن قرار میدهیم:
1import Link from 'next/link'
2
3const Index = () => (
4 <div>
5 <h1>Home page</h1>
6 <Link href='/blog'>
7 <a>Blog</a>
8 </Link>
9 </div>
10)
11
12export default Index
اکنون اگر تلاش کنیم کاری که در بخش قبلی انجام دادیم را مجدداً اجرا کنیم، میبینیم که وقتی به صفحه بلاگ میرویم، تنها bundle به نام blog.js بارگذاری میشود:
اکنون صفحه بسیار سریعتر از قبل بارگذاری میشود و اسپینر بارگذاری مرورگر در زبانه جدید اصلاً دیده نمیشود. چنان که میبینید URL نیز تغییر یافته است. این وضعیت به صورت یکپارچهای با History API مرورگر نیز سازگار است. در واقع این مفهوم عملی رندرینگ سمت کلاینت است. در این حالت اگر دکمه بازگشت را بزنید هیچچیزی بارگذاری نمیشود، زیرا مرورگر همچنان bundle به نام index.js قبلی را در اختیار دارد و آماده بارگذاری مسیر index/ است. چنان که میبینید همه چیز به صورت خودکار اجرا میشود. بدین ترتیب به پایان بخش دوم سری مقالات آموزش Next.js میرسیم. برای مطالعه بخش بعدی روی لینک زیر کلیک کنید:
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- مفاهیم مقدماتی و شیوه نصب Next.js — آموزش Next.js
- راهنمای جامع React (بخش اول) — از صفر تا صد
- هشت ترفند مفید برای توسعه اپلیکیشن های React — راهنمای کاربردی
==