
فهرست
مقدمه
صفحه ۱دانلود و نمایش دمو
نصب و اجرا
ویرایش و توسعه
جزییات نصب و اجرا ـ ۱
صفحه ۲عملیات Build
Start پروژه
جزییات نصب و اجرا ـ ۲
صفحه ۳استفاده از Node Stream
آمادهسازی Router
ارتباط با API
صفحه ۴تابع FetchData و پارامترهای آن
تابع API و پارامترهای آن
ارتباط کامپوننتهای React با دادههای API
صفحه ۵پیادهسازی Loading
صفحه ۶پیادهسازی تگهای سرصفحه
صفحه ۷پیادهسازی نقشهی سایت XML
صفحه ۸
در صفحههای قبلی، نحوهی دریافت، نصب و اجرا ، توسعهی پروژه و جزییات نصب و اجرای پروژه، توضیح داده شد. در این صفحه به سراغ نحوهی ارتباط پروژه با سرویس API میرویم.
ارتباط با API
به این علت که توضیحات پیشرو ممکن است در نگاه اول، کمی پیچیده بهنظر برسد، پیشنهاد میکنم ابتدا فایل server.mjs و فایل یکی از کامپوننتهایی که قصد دریافت دادهها از سرویس API در آن را داریم، به عنوان مثال فایل src/Pages/Category.jsx را با ویرایشگرتان باز کنید، حالا محل فراخوانی تابع FetchData
را پیدا کنید و مسیری را که برای دریافت دادهها از سرویس API طی میکند را دنبال کنید.

تابع FetchData و پارامترهای آن
پیش از آنکه ارتباط کامپوننتهای React، با دادههای دریافتی از API را توضیح بدهم، لازم است دربارهی تابع FetchData که وظیفهی دریافت دادهها از API را بر عهده دارد بنویسم.
تابع FetchData به کمک کتابخانهی Axios با API ارتباط برقرار میکند و دادهی درخواست شدهی مورد نیاز را بر میگرداند. این تابع به صورت async/await اجرا و فراخوانی شده است تا قبل از اجرای ادامهی کدها، تکلیف دریافت یا عدم دریافت دادهها از API را مشخص کرده باشیم.
این تابع در مسیر core/FetchData.mjs موجود است و در دو بخش مورد استفاده قرار خواهد گرفت: یکی در فایل server.mjs و دیگری در کامپوننتهایی که قصد نمایش دادههای API در آنها را داریم.
مقادیر بازگشتی این تابع، برای فایل server.mjs درون آرایهیی به نام firstData
قرار میگیرد و برای کامپوننتهای React درون یک آبجکت واحد. ↧
تابع FetchData شامل چهار پارامتر زیر است:
method
: متدهای HTTP شامل POST, GET, PUT, PATCH, DELETE، به شکل String
url
: آدرس URL مسیر API . به شکل String
dataForSend
: دادههایی است که میبایست به API ارسال شوند. به شکل String یا Object. میتواند خالی باشد.
serverMode
: در صورت استفاده در فایل server.mjs باید true و در صورت استفاده در کامپوننتها باید false باشد. به شکل boolean ↥
// core/FetchData.mjs
import axios from "axios";
const FetchData = async (method = 'get', url = '', dataForSend = '', serverMode = false) => {
try {
method = method?.toLowerCase();
let result = {};
let response;
if (!url || url === null)
return {};
let str = "";
if (dataForSend && typeof dataForSend === 'object') for (let item in dataForSend) str += `${item}=${dataForSend[item]}&`;
str = str.slice(0, -1); //last & remover
await axios({
method: method,
url: url + ((method === 'get') ? (typeof dataForSend === 'object') ? str : `?${dataForSend}` : ''),
data: (method === 'post' || method === 'put' || method === 'patch') ? dataForSend : {},
headers: { "Content-type": "application/json; charset=UTF-8" },
timeout: 20000,
}).then(function (res) {
response = res.data;
}).catch((err) => {
if (serverMode)
response = [];
else response = err?.response?.status || err?.code;
});
const data = response;
if (serverMode) {
result['firstData'] = data;
return result;
}
else return data;
} catch (err) {
if (serverMode)
return [];
return err.toString();
}
};
export default FetchData;
اگر ارتباط با API شما با Token است، میبایست آن را در آبجکت headers در پارامتر axios به همان شکلی که در مستندات سرویس API شما تعریف شده است، وارد کنید.(خط۱۸ کد بالا)
سرویس API این پروژه، یک سرویس Fake API رایگان است و نیاز به وارد کردن Token ندارد.
تابع API و پارامترهای آن
اما تابع FetchData چگونه پارامترهای خود را دریافت میکند؟ برای اینکه با توجه به آدرس URL درخواست شدهی کاربر به صورت پویا بتوانیم ورودی های تابع FetchData را تعیین کنیم، نیاز به تابع دیگری داریم که بتواند با توجه به آدرس URL درخواست شده توسط کاربر، Route های تعیین شده در App.jsx و همچنین مستندات سرویس API، خروجی مورد نظر را برای اعمال در پارامترهای تابع FetchData آماده کند؛ تابع API برای این منظور نوشته شده است.
تابع API یک الگوی واسطه است که کمک میکند با توجه به URL درخواست شدهی کاربر، method و url مورد نیاز برای دریافت دادهها از سرویس API برای انجام عملیات در تابع FetchData آماده شود.
تابع API در مسیر core/API.mjs موجود است و دو آرگومان میگیرد، یکی path
و دیگری urlSuffix
که urlSuffix
مخصوص فقط یکی از path ها یعنی single_products
است.
همانطور که گفتهشده، تابع FetchData در دو بخش مورد استفاده قرار خواهد گرفت: یکی در فایل server.mjs و دیگری در کامپوننتهایی که قصد نمایش دادههای API در آنها را داریم. (در صفحهی بعدی یعنی «ارتباط کامپوننتهای React با دادههای دریافتی از API» دربارهی تفاوت این دو، توضیح داده شده است.)
۱. توابع FetchData و API در فایل server.mjs:
۱.در فایل server.mjs ابتدا originalUrl
را دریافت میکنیم. (خط۸ کد زیر) ۲. برای سادهسازی، دایرکتوری ثابتی را که برای پروژه در نظر گرفتهایم را از ابتدای URL حذف میکنیم.(خط۹) ۳. اگر URL دریافتی شامل Query Strings
باشد، آنها را جدا میکنیم. (خط۱۰ و۱۱ و ۱۲) ۴. حالا تابع API را در ثابت apiInfo
فراخوانی میکنیم و ثابت path
که حالا بخشی از URL بدون Query Strings است را در پارامتر تابع API قرار میدهیم تا تابع API آبجکت مورد نیاز ما را برای برقراری ارتباط صحیح با سرویس API، برگرداند و آن آبجکت را درون ثابت apiInfo
قرار دهد. (خط۱۴) ۵. در انتها تابع FetchData، حالا پارامترهای مورد نیازش را دریافت کرده و آماده دریافت دادهها از سرویس API میشود. (خط۱۶)
ثابت dataForSend
که در خط ۱۲ کد زیر مشاهده میشود، شامل Query Strings است که در صورت وجود، درون آن قرار میگیرد تا به پارامتر موردنظر در تابع FetchData در خط ۱۶ وارد شود.
// server.mjs
import FetchData from './core/FetchData.mjs';
import API from './core/API.mjs';.
.
.
route.get('*', async (req, res) => {
try {
let url = req.originalUrl;
url = url.replace('/shop/', '');
url = url.split('?');
const path = url[0];
const dataForSend = url[1];
const apiInfo = API(path ? path : 'Index');
const dataFromServer = await FetchData(apiInfo?.method, apiInfo?.url, dataForSend, true);.
.
.
در خط ۱۴ کد بالا شرطی اعمال شده که اگر
path
خالی باشد ـ یعنی URL ما شامل دایرکتوری نباشد و در واقع همان صفحهی اصلی یا خانه باشد ـIndex
به پارامتر path تابع API که در کد زیر مشاهده میشود، ارسال شود. که همانطور که در کد زیر میبینید کلیدIndex
شامل یک آبجت برای دریافت اطلاعات صفحهی Home Page از سرویس API است.
// core/API.mjs
const API = (path, urlSuffix = "") => {
try {
if (path.split('/')[path.split('/').length - 2] === 'products') {
urlSuffix = path.split('/')[path.split('/').length - 1]
path = 'single_products'
}
const baseUrl = 'https://fakeapi.platzi.com';
const APIInfo = {
//Home page:
"Index": {
method: 'get',
url: baseUrl + '/products'
},
"products": {
method: 'get',
url: baseUrl + '/products'
},
"single_products": {
method: 'get',
url: baseUrl + '/products/' + urlSuffix
},
"category/men's%20clothing": {
method: 'get',
url: baseUrl + '/products/category/' + "men's clothing"
},
"category/electronics": {
method: 'get',
url: baseUrl + '/products/category/' + "electronics"
},
"category/jewelery": {
method: 'get',
url: baseUrl + '/products/category/' + "jewelery"
},
"category/women's%20clothing": {
method: 'get',
url: baseUrl + '/products/category/' + "women's clothing"
}
}
return APIInfo?.[path] || { method: '', url: '' };
} catch (err) {
return {
method: '',
url: ''
}
}
}
export default API;
ساختار تابع API موجود در فایل core/API.mjs که در کد بالا نیز مشاهده میشود با توجه به ساختار سرویس API موجود و Route های موجود در App.jsx طراحی شده است. پس طبیعتاً برای هر سرویس API دیگری و هر ساختار Route دیگری باید با کد بالا متفاوت باشد و با توجه به چارچوب آنها دوباره طراحی شود.
به عنوان مثال در خط ۴ کد بالا تعیین کردهایم که اگر دایرکتوری یکی مانده به آخر URL درخواست شده،products
بود:path
بایدsingle_products
باشد پس این تابع، باید آبجکتsingle_products
را برگرداند.
و در انتهای کلیدurl
در آبجکتsingle_products
بهurlSuffix
برای دریافت از سرویس API اشاره شده، کهurlSuffix
مساوی با آخرین بخش دریافتی از URL درخواست شده، مقداردهی شده است.(خط۵کد بالا)
۲. توابع FetchData و API در کامپوننتها:
در کامپوننت موردنظرمان، برای مشخص کردن آرگومان مربوط به پارامتر path تابع API ، مثلا اگر در کامپوننت Category هستیم به شکل زیر عمل میکنیم:
// src/pages/Category.jsx
import { useParams } from "react-router-dom";
import API from "../../core/API.mjs";
const Category = ({ dataFromServer }) => {
const params = useParams();
const name = params?.name;
const apiInfo = API(`category/${name}`);
.
.
.
در کد بالا از useParams
استفاده کردیم که وظیفه دارد param هایی که قبلا در تعریف Route مشخص کردهایم را صدا بزند. به عنوان مثال param همین کامپوننت را قبلا در path
(یک props از Routeهای کامپوننت App) ، به شکل زیر تعریف کردهایم.
:name
که یک param در کد زیر است، در کامپوننت Category
ما (کد بالا) با استفاده از useParams
در دسترس خواهد بود.
// src/App.jsx
.
.
<Route path="/category/:name" element={<Category dataFromServer={dataFromServer} />} />
.
.
:name
مساوی با همان چیزی است که کاربر در بخش مشخصشدهی:name
در URL درخواست کرده است. مثلا برایhttp://localhost:5173/shop/category/electronics
مقدار:name
مساوی است باelectronics
یا برایhttp://localhost:5173/shop/category/jewelery
مقدار:name
مساوی است باjewelery
.
حالا ثابت apiInfo
در مثال بالا، باید آبجکتی را [از تابع API] برگرداند که مربوط به یکی از Categoryها است (مثلا electronics) ؛ این آبجکت شامل کلیدها و مقدارهای method
و url
خواهد بود:
// core/API.mjs
const API = (path) => {
.
.
const baseUrl = 'https://api-url';
const APIInfo = {
.
.
"category/electronics": {
method: 'get',
url: baseUrl + '/products/category/electronics'
}
.
.
};
return APIInfo?.[path];
}
method
و url
دریافت شده، حالا باید به پارامترهای تابع FetchData فرستاده شود تا از سرویس API، دادههای درخواست شده را برگرداند.
// src/pages/Category.jsx
import { useParams } from "react-router-dom";
import API from "../../core/API.mjs";
import FetchData from "../../core/FetchData.mjs";
const Category = ({ dataFromServer }) => {
const params = useParams();
let name = params?.name;
const apiInfo = API(`category/${name}`);
.
.
.
response = await FetchData(apiInfo?.method, apiInfo?.url);
.
.
.
صفحهی قبل: جزییات نصب و اجرا ـ ۲
صفحهی بعد: ارتباط کامپوننتهای React با دادههای دریافتی از API
اطلاعات برنامه
نام: express-react-ssr
پلتفرم: Node.js
زبان: JavaScript
لایسنس: MIT
تاریخ انتشار اولیه: ۹ مرداد ۱۴۰۳
تاریخ آخرین بهروزرسانی: ۱۱ شهریور ۱۴۰۳
مخزن: GitHub