فهرست
مقدمه
صفحه ۱دانلود و نمایش دمو
نصب و اجرا
ویرایش و توسعه
جزییات نصب و اجرا ـ ۱
صفحه ۲عملیات 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