CSS و مسیرهای API در Next.js — آموزش Next.js (بخش چهارم)
در این بخش از سری مقالات آموزش Next.js به بررسی شیوه استایلدهی به کامپوننتهای Next صحبت میکنیم. در این مسیر با شیوه استفاده از CSS و مسیرهای API در Next.js آشنا خواهیم شد. برای مطالعه بخش قبلی این سری مقالات میتوانید روی لینک زیر کلیک کنید:
برای استایلدهی به کامپوننتهای Next.js آزادی عمل زیادی داریم، زیرا میتوانیم از هر کتابخانهای که دوست داریم استفاده کنیم، اما Next.js یک کتابخانه داخلی به نام styled-jsx (+) دارد که از سوی همان تیم توسعه فریمورک Next طراحی شده است.
n
این کتابخانه امکان نوشتن CSS دامنهدار را فراهم میسازد که از نظر قابلیت نگهداری کد عالی است، زیرا CSS تنها روی کامپوننتی که اعمال شده است، باقی میماند. این رویکرد مناسبی برای نوشتن CSS محسوب میشود و دیگر نیازی به استفاده از کتابخانه یا پیشپردازشگرهای دیگر که بر پیچیدگی قضیه میافزایند، وجود نخواهد داشت. در Next.js برای افزودن CSS به کامپوننت React، آن را درون یک قطعه کد در JSX درج میکنیم که با عبارت زیر آغاز میشود:
1<style jsx>{`
و با عبارت زیر پایان مییابد:
1}</style>
درون این دو بخش آغازین و پایانی از CSS معمولی استفاده میکنیم و در نهایت آن را در یک فایل .css قرار میدهیم:
1<style jsx>{`
2 h1 {
3 font-size: 3rem;
4 }
5`}</style>
این کد را درون JSX به صورت زیر مینویسیم:
1const Index = () => (
2 <div>
3 <h1>Home page</h1>
4
5 <style jsx>{`
6 h1 {
7 font-size: 3rem;
8 }
9 `}</style>
10 </div>
11)
12
13export default Index
درون بلوک میتوانیم از «میانیابی» (Interpolation) برای تغییر دینامیک مقادیر استفاده کنیم. برای نمونه در این کد فرض میکنیم که prop به نام size از سوی کامپوننت والد ارسال شده است و از آن در بلوک styled-jsx استفاده میکنیم:
1const Index = props => (
2 <div>
3 <h1>Home page</h1>
4
5 <style jsx>{`
6 h1 {
7 font-size: ${props.size}rem;
8 }
9 `}</style>
10 </div>
11)
اگر میخواهید برخی کدهای CSS را به صورت سراسری اعمال کنید، و محدود به دامنه یک کامپوننت نباشید، میتوانید کلیدواژه global را در تگ style اضافه کنید:
1<style jsx global>{`
2body {
3 margin: 0;
4}
5`}</style>
اگر میخواهید یک فایل CSS اکسترنال را در کامپوننت Next.js ایمپورت کنید، باید ابتدا zeit/next-css@ را نصب کنید:
1npm install @zeit/next-css
و سپس فایل پیکربندی را در ریشه پروژه به نام next.config.js با محتوای زیر بسازید:
1const withCSS = require('@zeit/next-css')
2module.exports = withCSS()
پس از ریاستارت کردن اپلیکیشن Next میتوانید CSS را به صورت معمول چنان که در کتابخانهها یا کامپوننتهای جاوا اسکریپت انجام میشود، ایمپورت کنید:
1import '../style.css'
همچنین میتوانید یک SASS را به صورت مستقیم با استفاده از کتابخانه zeit/next-sass@ ایمپورت کنید.
مقداردهی تگ head با تگهای سفارشی
در هر کامپوننت صفحه Next.js میتوانید اطلاعاتی به هدر صفحه اضافه کرد. این ویژگی در موارد زیر به کار میآید:
- بخواهید عنوان صفحهای را سفارشیسازی کنید.
- بخواهید تگ متا را تغییر دهید.
طرز کار این گونه است که درون هر کامپوننت میتوانید کامپوننت Head را از next/head ایمپورت کرده و آن را در خروجی JSX کامپوننت خود بگنجانید:
1import Head from 'next/head'
2
3const House = props => (
4 <div>
5 <Head>
6 <title>The page title</title>
7 </Head>
8 {/* the rest of the JSX */}
9 </div>
10)
11
12export default House
همچنین میتوانید هر تگ HTML را که دوست دارید در بخش <head> صفحه ظاهر شود، اضافه کنید. زمانی که کامپوننت نصب شد، Next.js اطمینان حاصل میکند که تگهای درون Head به عنوان صفحه اضافه شده باشند. همین وضعیت در مورد زمان حذف کامپوننت از صفحه نیز صدق میکند و Next.js وظیفه حذف کردن آن تگها را بر عهده میگیرد.
افزودن یک کامپوننت پوششی
همه صفحههای سایت کمابیش شبیه هم هستند. یک پنجره کروم و یک لایه مبنای مشترک وجود دارد و میخواهیم تنها آن چه را که درون آن قرار دارد ویرایش کنیم.
درون صفحه عموماً یک نوار ناوبری، یک نوار کناری و سپس محتوای اصلی قرار میگیرد. برای ساختن چنین سیستمی در Next.js یک روش این است که از «کامپوننت مرتبه بالاتر» (Higher Order Component) استفاده کرده و اقدام به ساخت یک کامپوننت مانند components/Layout.js بکنیم:
1export default Page => {
2 return () => (
3 <div>
4 <nav>
5 <ul>....</ul>
6 </hav>
7 <main>
8 <Page />
9 </main>
10 </div>
11 )
12}
در این کامپوننت میتوانیم کامپوننتهای مجزایی را برای عنوان و/یا نوار ناوبری ایمپورت کرده و همه CSS مورد نیاز را اضافه نماییم. همچنین میتوانیم در همه صفحهها به صورت زیر از آن استفاده کنیم:
1import withLayout from '../components/Layout.js'
2
3const Page = () => <p>Here's a page!</p>
4
5export default withLayout(Page)
اما این وضعیت تنها برای کاربردهای ساده که نیازی به فراخوانی ()getInitialProps روی همه صفحهها وجود ندارد، به کار میآید. زیرا ()getInitialProps تنها روی کامپوننت صفحه فراخوانی میشود. اما اگر کامپوننت مرتبه بالاتر ()withLayout را از یک صفحه اکسپورت کنیم، ()Page.getInitialProps فراخوانی نمیشود. در حالی که ()withLayout.getInitialProps چنین کاری را انجام میدهد.
برای جلوگیری از پیچیده سازی غیرضروری کدبیس، رویکرد جایگزین استفاده از prop-ها است:
1export default props => (
2 <div>
3 <nav>
4 <ul>....</ul>
5 </hav>
6 <main>
7 {props.content}
8 </main>
9 </div>
10)
هم اینک میتوانیم در صفحههای خود از آن به صورت زیر استفاده کنیم:
1import Layout from '../components/Layout.js'
2
3const Page = () => (
4 <Layout content={(
5 <p>Here's a page!</p>
6 )} />
7)
این رویکرد به ما امکان میدهد که ()getInitialProps را از درون کامپوننت صفحه مورد استفاده قرار دهیم. تنها عیب آن این است که باید JSX کامپوننت را درون prop به نام content بنویسیم:
1import Layout from '../components/Layout.js'
2
3const Page = () => (
4 <Layout content={(
5 <p>Here's a page!</p>
6 )} />
7)
8
9Page.getInitialProps = ({ query }) => {
10 //...
11}
مسیرهای API
علاوه بر ایجاد «مسیرهای صفحه» (page routes) که صفحههایی هستند که برای عرضه به صورت صفحههای وب در اختیار مرورگر قرار میگیرند، Next.js میتواند مسیرهای API را نیز ایجاد کند. این یکی از جالبترین قابلیتها است، زیرا Next.js به وسیله آن میتواند برای ایجاد فرانتاند برای دادههایی که توسط خود Next.js تولید و بازیابی میشوند مورد استفاده قرار گیرد و JSON را از طریق درخواستهای واکشی انتقال میدهد. مسیرهای API زیر پوشه /pages/api/ قرار دارند و به نقطه انتهایی api/ نگاشت میشوند.
این قابلیت در زمانی که اپلیکیشن میسازیم، بسیار مفید خواهد بود. در این مسیرها کد Node.js را (به جای کد React) مینویسیم و بدین ترتیب با یک «جابجایی گفتمان» بدون وقفه از فرانتاند به بکاند جابجا میشویم. فرض کنید یک فایل به نام pages/api/comments.js/ دارید که هدف آن بازگشت دادن کامنتهای یک نوشته بلاگ به صورت JSON است. همچنین تصور کنید لیستی از کامنت ها در فایل comments.json ذخیره شدهاند:
1[
2 {
3 "comment": "First"
4 },
5 {
6 "comment": "Nice post"
7 }
8]
کد نمونه که لیست کامنتها را به کلاینت باز میگرداند، به صورت زیر است:
1import comments from './comments.json'
2
3export default (req, res) => {
4 res.status(200).json(comments)
5}
این کد روی URL به صورت api/comments/ گوش میدهد تا درخواستهای GET را بشنود. میتوانید آن را با استفاده از مرورگر فراخوانی کنید:
مسیرهای API همچنین برای مسیریابی دینامیک همانند مسیرهای صفحهها استفاده میشوند. با استفاده از ساختار [] میتوانید مسیرهای API دینامیک مانند pages/api/comments/[id].js/ بسازید که کامنت های خاص را برای شناسه یک نوشته بازمیگردانند. درون [id].js میتوان مقدار id را با گشتن درون شیء req.query بازیابی کرد:
1import comments from '../comments.json'
2
3export default (req, res) => {
4 res.status(200).json({ post: req.query.id, comments })
5}
در تصویر زیر اجرای کد فوق را میبینید:
در صفحههای دینامیک باید useRouter را از next/router ایمپورت کنید و سپس شیء روتر را با استفاده از ()const router = useRouter دریافت کنید تا بتوانید مقدار id را با استفاده از router.query.id به دست آورید. در سمت سرور کارها آسانتر است، چون کوئری به شیء درخواست الصاق یافته است. اگر یک درخواست POST ارسال کنید، طرز کار به همان صورت خواهد بود و همه چیز از طریق اکسپورت پیشفرض انجام مییابد. برای جداسازی POST از GET و دیگر متدهای HTTP (مانند PUT و DELETE) مقدار req.method را بررسی کنید:
1export default (req, res) => {
2 switch (req.method) {
3 case 'GET':
4 //...
5 break
6 case 'POST':
7 //...
8 break
9 default:
10 res.status(405).end() //Method Not Allowed
11 break
12 }
13}
علاوه بر req.query و req.method که قبلاً دیدیم، از طریق ارجاع به req.cookies به کوکیها و از طریق req.body به بدنه درخواست هم دسترسی داریم. همه این کارها در پشت صحنه به وسیله Micro (+) انجام مییابند که کتابخانهای برای توانمندسازی میکروسرویس های HTTP ناهمگام است و از سوی همان تیم توسعه Next.js ارائه شده است. شما میتوانید از میانافزار Micro در مسیرهای API برای افزایش کارکردها بهره بگیرید.
اجرای کد صرفاً در سمت کلاینت یا سرور
در کامپوننتهای صفحه میتوانید با بررسی مشخصه window کد را تنها در سمت کلاینت یا سرور اجرا کنید. این مشخصه تنها در مرورگر وجود دارد و از این رو میتوانید آن را به صورت زیر بررسی کنید:
1if (typeof window === 'undefined') {
2
3}
و کد سمت سرور را به این بلوک اضافه کنید. به طور مشابه میتوانید کد سمت کلاینت را تنها با نوشتن کد زیر اجرا کنید:
1if (typeof window !== 'undefined') {
2
3}
نکته جاوا اسکریپت: ما در اینجا از عملگر typeof استفاده کردهایم، زیرا نمیتوانیم تعریف نشده بودن مقدار را به روشهای دیگر تشخیص دهیم. امکان اجرای کد زیر وجود ندارد:
1if (window === undefined)
زیرا با خطای زمان اجرای window is not defined مواجه خواهیم شد. همچنین Next.js به عنوان بهینهسازی زمان build کدی را که از این بررسیها استفاده میکند از bundle-ها حذف میکند. یک باندل سمت کلاینت محتوای قرار گرفته درون بلوک زیر را شامل نمیشود:
1if (typeof window === 'undefined') {}
بدین ترتیب به پایان این بخش از مقالات آموزش Next.js میرسیم. در بخش بعدی که آخرین بخش محسوب میشود، در مورد انتشار نسخه پروداکشن اپلیکیشن و تحلیل باندل صحبت میکنیم. برای مطالعه بخش بعدی روی لینک زیر کلیک کنید:
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- آموزش JavaScript ES6 (جاوا اسکریپت)
- مفاهیم مقدماتی و شیوه نصب Next.js — آموزش Next.js (بخش اول)
- آموزش ری اکت (React) — مجموعه مقالات مجله فرادرس
==