Web Worker ها ابزاری برای مرورگرهای وب برای اجرای اسکریپت ها در پس زمینه بدون تداخل با UI هستند. اگر به درستی راهاندازی شود، یکweb worker میتواند پیامهایی را از کد جاوا اسکریپت یا تایپ اسکریپت که در آن راهاندازی شده است ارسال و دریافت کند. در این مقاله، ما در مورد web worker ها و نحوه استفاده از آنها در پروژه با React و TypeScript با ساخت جدول دادههای صفحهبندی شده، آشنا میشویم.
ما length ،filter ،cut و map را از طریق آرایه های طولانی بدون تأثیر بر عملکرد یا UX برنامه های وب خود تعیین می کنیم. اساساً، ما یاد میگیریم که چگونه با استفاده از web worker ها، عملکرد یک برنامه وب را افزایش دهیم، که به ما قابلیت چند رشتهای را برای مدیریت فرآیندهای طولانی در پسزمینه ارائه میدهد.
Web Worker ها چیست؟
web worker ها یک فرآیند جاوا اسکریپت است که در پسزمینه یک صفحه وب اجرا میشود. به طور پیش فرض جاوا اسکریپت تک رشته ای است. در نتیجه، جاوا اسکریپت فقط یک رشته یا مسیر اجرا دارد، رشته اصلی، که به طور همزمان تمام کدهای جاوا اسکریپت را در یک صفحه وب اجرا می کند.
از طرف دیگر، Web Workers یک ویژگی مرورگر است که به شما امکان می دهد چندین رشته جاوا اسکریپت را به صورت موازی بدون تداخل با DOM اجرا کنید.
برخلاف رشته اصلی، web worker ها نمیتوانند به DOM دسترسی داشته باشند و آنها را دستکاری کنند، اما میتوانند I/O را با استفاده از XMLHttpRequest یا Fetch انجام دهند. به جای اینکه رشته اصلی باید تمام کدهای جاوا اسکریپت را اجرا کند، میتوانیم یک web worker ایجاد کنیم تا محاسبات طولانیمدت را مدیریت کند و اطمینان حاصل کنیم که رشته اصلی مشکلی پیدا نکرده است.
برای ایجاد یک آبجکت worker، می توانید از سازنده ()worker استفاده کنید. به خاطر داشته باشید که web worker ها در یک context گلوبال اجرا می شوند که با شی window متفاوت است.
در جاوا اسکریپت، web worker ها توسعهدهندگان را قادر میسازند تا به multi-threading یا همزمانی واقعی دست یابند. سیستمهای همزمان با استفاده از مدلهای همزمان مختلف پیادهسازی میشوند که مشخص میکنند رشتهها چگونه باید برای تکمیل یک کار تعامل داشته باشند. یکی از جنبههای مهم مدلهای همزمان این است که آیا نخها حالت مشترک دارند یا ایزوله هستند.
جاوا اسکریپت از دو مدل همزمان، مدل actor و حافظه مشترک پشتیبانی می کند. هر دوی این مدلهای همزمان دارای مزایا و معایب هستند، از worker های متفاوت استفاده میکنند و اغلب تحت عنوان معماری «off main thread» گروهبندی میشوند.
از سال 2023، web worker ها به طور کامل توسط همه مرورگرهای اصلی پشتیبانی می شوند.
توجه داشته باشید که شما واقعاً فقط در صورتی نیاز به استفاده از web worker دارید که سایت شما نیاز به انجام کارهای سنگین داشته باشد.
راه اندازی پروژه وب ورکر ها React و TypeScript
برای راه اندازی پروژه خود، یک پوشه جدید ایجاد می کنیم که حاوی کد ما باشد. اسم برنامه خودم را می گذارم، پوشه جدید را در ویرایشگر کد مورد نظر خود باز کنید، سپس کد زیر را در ترمینال خود اجرا کنید تا یک پروژه React و TypeScript راه اندازی شود:
npx create-react-app ./ --template TypeScript
همچنین می توانید پروژه خود را با Vite نیز بسازید جهت آشنایی با نحوه ایجاد پروژه vite با react این مقاله را بررسی کنید.
با راه اندازی پروژه، ما سه پوشه در داخل src ایجاد می کنیم:
components : که Loader.tsx، Pagination.tsx، و Table.tsx را ذخیره می کندdata: فایل index.ts را ذخیره می کندlongProcess:که count.ts ،enums.ts و getData.ts را ذخیره می کند.
ساختار پوشه و فایل ما باید شبیه به موارد زیر باشد:
app
├── README.md
├── node_modules
├── package.json
├── tsconfig.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
├──components
│ ├── Loader.tsx
│ ├── Pagination.tsx
│ └── Table.tsx
|
├──data
│ └── index.ts
|
├──longProcesses
│ ├── count.ts
│ ├── enums.ts
│ └── getData.ts
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
├── serviceWorker.js
└── setupTests.js
برای راه اندازی سرور توسعه خود، npm start یا yarn start را در ترمینال اجرا کنید.
آماده سازی داده های TypeScript
در data/index.ts، یک آرایه طولانی اضافه میکنیم که شامل 5000 آبجکت است که شبیه موارد زیر است:
{
"albumId": 1,
"id": 2,
"title": "reprehenderit est deserunt velit ipsam",
"url": "https://via.placeholder.com/600/771796",
"thumbnailUrl": "https://via.placeholder.com/150/771796"
},
ایجاد enums
Enums مجموعه ای از ثابت ها یا متغیرهای نامگذاری شده را تعریف می کند که تغییر نمی کنند. enum یک کلاس خاص در TypeScript است که نشان دهنده گروهی از متغیرهای غیرقابل تغییر است، مانند یک شی با یک متد فریز که روی آن فراخوانی می شود. Enum ها اطمینان حاصل می کنند که همیشه از نام متغیرهای مناسب در کد خود استفاده می کنید و از خطاهایی که ممکن است هنگام املایی یک متغیر رخ دهد جلوگیری می کند.
برای اطمینان از فراخوانی متغیرهای صحیح، باید processList و ProfileEnum را در longProcesses/enums.ts ایجاد و export کنیم، enum هایی که در فایل ها استفاده می شوند:
// longProcesses/enums.ts
export enum processList {
count = "count",
getData = "getData",
}
export enum ProfileEnum {
albumId = "Album Id",
id = "ID",
title = "Title",
url = "Url",
thumbnailUrl = "Thumbnail",
}
جهت بررسی چرا تایپ اسکریپت برای پروژه های بزرگ خوب است، این مقاله را بررسی کنید.
ساخت انواع App.ts و Generate
یکی از مهم ترین ویژگی های TypeScript توانایی آن در بررسی انواع است. ما می توانیم انواع را برای هر متغیر، پارامتر، شی یا آرایه تعریف کنیم و اطمینان حاصل کنیم که همیشه از نوع داده مناسب استفاده می کنیم. برای درک بهتر انواع در تایپ اسکریپت این مقاله را بررسی کنید.
App.ts نقطه ورود برنامه ما است. در اینجا چهار نوع LengthCountType، ProfileType، ProfileListType و GetDataType را تعریف می کنیم. ما همچنین یک کامپوننت تابعی ایجاد خواهیم کرد که یک jsx را با دو کامپوننت بخش تو در تو در یک div برمی گرداند، همانطور که در کد زیر نشان داده شده است:
// App.ts
type LengthCountType = {
loading: boolean;
value: number;
};
export type ProfileType = {
albumId: number | string;
id: number | string;
title: string;
url: string;
thumbnailUrl: string;
};
export type ProfileListType = {
loading: boolean;
list: unknown & Array<ProfileType>;
page: number;
};
export type GetDataType = {
action: string;
period: "initial" | "next" | "prev" | "pageNumber";
thePageNumber: number;
};
export const listPageSize = 50;
const App = () => {
return (
<main className="main-container">
<section className="count"></section>
<section className="table-container"></section>
</main>
)
}
جهت آشنایی با Generic ها در تایپ اسکریپت این مقاله را بررسی کنید.
طراحی we worker ها برای بررسی طول آرایه ها
تعیین طول یک آرایه طولانی، رشته منفرد مورد استفاده توسط یک برنامه وب را مسدود می کند. با این حال، با یک we worker ها، میتوانیم با موفقیت طول یک آرایه طولانی را بدون مسدود کردن رشته بررسی کنیم. ما کد این مورد را در longProcesses/count.ts اضافه می کنیم:
// longProcesses/count.ts
/* eslint-disable no-restricted-globals */
import { profiles } from "../data";
import { processList } from "./enums";
self.onmessage = (e: MessageEvent<string>) => {
if (e.data === processList.count) {
const findLength = profiles.length;
self.postMessage(findLength);
}
};
export {};
ابتدا خطایی که می گوید از self اشتباه استفاده کرده ایم را غیرفعال می کنیم. بعد، آرایه و enum processList را وارد می کنیم. سپس، به رویداد onmessage گوش میدهیم، که وقتی پیامی در فایل worker پست میشود، فعال میشود.
بررسی کنید که آیا داده های ارسال شده معادل تعداد شمارش است یا خیر. اگر اینطور باشد، طول داده ها را دریافت کرده و آن را به فایل اصلی در DOM ارسال می کنیم. در نهایت، برای جلوگیری از خطای TypeScript، یک شی خالی export کنید.
ایجاد کامپوننت Loader
کامپوننت Loader نشان می دهد که فرآیند در پس زمینه پردازش می شود. در components/Loader.ts کد زیر را اضافه کنید:
// components/Loader.ts
import React from "react";
type Props = {
color?: "black" | "white";
size?: number;
display?: "inline-block" | "block";
};
const Loader = ({
color = "black",
size = 20,
display = "inline-block",
}: Props) => {
return (
<div
style={{
border: `4px solid ${color}`,
width: `${size}px`,
height: `${size}px`,
borderRightColor: "transparent",
borderRadius: "50%",
display,
margin: display === "block" ? "50px auto" : "none",
}}
className="loader"
></div>
);
};
export default Loader;
بیایید آن را تجزیه کنیم. Loader سه پراپس، color، اندازه و display را می پذیرد. یک div را با استایل ها برمی گرداند. همچنین دارای یک کلاس loader است که در index.css استایل داده شده است تا یک حرکت چرخشی ایجاد کند:
// index.css
.loader {
animation: spinner 1s linear forwards infinite;
}
@keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
استفاده از count web worker در TypeScript
count web worker طول آرایه طولانی ما را تعیین می کند. برای استفاده از count worker، App.ts را باز کنید و useMemo ،useEffect، و هوک useState را از React و processList وارد کنید:
// App.ts
import React, { useEffect, useMemo, useRef, useState } from "react";
import { processList } from "./longProcesses/enums";
در داخل کامپوننت، یک web worker جدید را با فایل worker count.ts که قبلا ایجاد کرده بودیم، مقداردهی اولیه می کنیم:
// App.ts
// const App = () => {
const counter: Worker = useMemo(
() => new Worker(new URL("./longProcesses/count.ts", import.meta.url)),
[]
);
در مرحله بعد، وضعیتی ایجاد می کنیم که طول آرایه را به صورت محلی برای ما ذخیره می کند:
// App.ts
const [lengthCount, setLengthCount] = useState<LengthCountType>({
loading: true,
value: 0,
});
ما از شمارنده اولیه برای ارسال پیام به count worker در داخل useEffect Hook استفاده می کنیم:
// App.ts
useEffect(() => {
if (window.Worker) {
counter.postMessage(processList.count);
}
}, [counter]);
سپس، مجموعه ای از مقادیر مناسب را در استیت lengthCount ایجاد می کنیم:
useEffect(() => {
if (window.Worker) {
counter.onmessage = (e: MessageEvent<string>) => {
setLengthCount((prev) => ({
...prev,
loading: false,
value: Number(e.data) && Number(e.data),
}));
};
}
}, [counter]);
در نهایت، از استیت lengthCount برای رندر کردن یک لودر و طول آرایه در رابط کاربری استفاده میکنیم:
// App.ts
<section className="count">
Total count of Profiles is{" "}
<b>{lengthCount.loading ? <Loader size={14} /> : lengthCount.value}</b>
</section>
در متغیر شمارنده، نمونه weorker را با useMemo میپیچیم و از شروع مجدد غیرضروری در رندرهای مجدد جلوگیری میکنیم. در داخل تابع، از متد new Worker استفاده می کنیم و نمونه جدیدی از URL تولید شده را که حاوی مسیر فایل count worker است، ارسال می کنیم.
حالت lengthCount وضعیت بارگذاری و مقدار شمارنده را نگه می دارد. اولین useEffect count worker را در اولین رندر مجدد اجرا می کند. اساساً، قبل از ارسال پیام به فایل count worker، بررسی میکند که آیا مرورگر کاربر از web worker ها پشتیبانی میکند یا خیر.
useEffect دوم پاسخی را از count worker دریافت می کند و پاسخ دریافتی را روی حالت lengthCount قرار می دهد.
در نشانهگذاری، زمانی که فرآیند هنوز در حال اجرا است، یک لودر و همچنین مقدار آن را پس از تکمیل نمایش میدهیم.
یک کارگر در TypeScript برای دریافت داده ایجاد کنید
دریافت داده از یک آرایه طولانی مانند آرایهای که ما داریم میتواند باعث تاخیر در دستگاههای کاربر و در نتیجه UX بد شود. ما می توانیم با یک web worker از آن جلوگیری کنیم.
برای شروع، کد زیر را به longProcesses/getData.ts اضافه می کنیم:
// getData.ts
/* eslint-disable no-restricted-globals */
import { GetDataType, listPageSize, ProfileListType } from "../App";
import { profiles } from "../data";
import { processList } from "./enums";
self.onmessage = (e: MessageEvent<string>) => {
const data = JSON.parse(e.data) as GetDataType;
if (data.action !== processList.getData) {
return;
}
if (data.period === "initial") {
const items = profiles.filter((item, index) => index < listPageSize);
const response = {
loading: false,
list: items,
page: data.thePageNumber,
} as ProfileListType;
self.postMessage(JSON.stringify(response));
}
}
export {};
ابتدا انواع تعریف شده را در App.ts، لیست داده ها و processList وارد می کنیم. سپس، داده های برگشتی را در رویداد تجزیه می کنیم و آن را به عنوان GetDataType تنظیم می کنیم. بررسی می کنیم که آیا نوع action نامعتبر است و آیا دوره action آغاز شده است یا خیر. اگر چنین است، ما 50 مورد اول لیست خود را با استفاده از روش فیلتر جاوا اسکریپت دریافت می کنیم، یک شی پاسخ ایجاد می کنیم و سپس آن را به عنوان یک رشته پست می کنیم.
ساختن یک جدول
جدول لیست موارد موجود در رابط کاربری را نشان می دهد. در components/Table.tsx کد زیر را اضافه کنید:
import React from "react";
import { ProfileType } from "../App";
import { ProfileEnum } from "../longProcesses/enums";
type Props = {
list: Array<ProfileType>;
};
const Table = ({ list }: Props) => {
return (
<div className="table">
<table>
<thead>
<tr>
<th>#</th>
<th>{ProfileEnum.id}</th>
<th>{ProfileEnum.title}</th>
<th>{ProfileEnum.albumId}</th>
<th>{ProfileEnum.thumbnailUrl}</th>
</tr>
</thead>
<tbody>
{list.length > 0 &&
list.map((item, index: number) => {
return (
<tr key={item?.id}>
<td>{index + 1}</td>
<td>{item?.id}</td>
<td>{item?.title}</td>
<td>{item?.albumId}</td>
<td>
<img
src={item?.thumbnailUrl}
alt={item?.title}
width={50}
height={50}
loading="lazy"
/>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
};
export default Table;
ابتدا ProfileType و ProfileEnum را وارد می کنیم، سپس PropType را برای کامپوننت Table خود تعریف می کنیم. بعد، یک div ایجاد می کنیم که یک جدول را برمی گرداند:
table: یک thead و یک tbody را برمی گرداندthead: یک tr را برمیگرداند که حاوی پنج تا th استtbody: حاوی map ای است که tr را برمی گرداندtr: پنج تا td را برمی گرداند
برای افزایش عملکرد، از بارگذاری lazy برای تولید تصاویر با استفاده از ویژگی "loading="lazy استفاده کردیم.
استفاده از web worker ها getData با TypeScript
استفاده از web worker getData مشابه نحوه استفاده ما از count worker است. ابتدا یک کارگر جدید را با کد زیر مقداردهی اولیه می کنیم:
// App.ts
const getData: Worker = useMemo(
() => new Worker(new URL("./longProcesses/getData.ts", import.meta.url)),
[]
);
در مرحله بعد، state محلی را برای مدیریت وضعیت profileList تعریف می کنیم:
// App.ts
const [profileList, setProfileList] = useState<ProfileListType>({
loading: true,
list: [],
page: 1,
});
سپس، یک useEffect ایجاد می کنیم که در آن از getData در رندر اولیه استفاده می کنیم:
// App.ts
useEffect(() => {
if (window.Worker) {
const request = {
action: processList.getData,
period: "initial",
thePageNumber: profileList.page,
} as GetDataType;
getData.postMessage(JSON.stringify(request));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
ما یک useEffect دیگر ایجاد خواهیم کرد که به دریافت پاسخ از فایل web worker getData رسیدگی می کند:
// App.ts
useEffect(() => {
if (window.Worker) {
getData.onmessage = (e: MessageEvent<string>) => {
const response = JSON.parse(e.data) as unknown as ProfileListType;
setProfileList((prev) => ({
...prev,
loading: response.loading,
list: response.list,
page: response.page,
}));
};
}
}, [getData]);
در نهایت، داده ها را با استفاده از کامپوننت Table که ایجاد کردیم نمایش می دهیم:
<section className="table-container">
{profileList.loading ? (
<Loader size={40} display="block" />
) : (
<>
<Table list={profileList.list} />
</>
)}
</section>
در متغیر getData، useMemo نمونه کارگر را میپیچد تا از شروع مجدد غیرضروری در رندرهای مجدد جلوگیری کند. در داخل تابع، از متد جدید Worker استفاده می کنیم و یک نمونه جدید از URL تولید شده را که حاوی مسیر فایل workData worker است، ارسال می کنیم. وضعیت profileList وضعیت بارگیری، فهرست و صفحه کارگر getData را به صورت محلی در برنامه ما نگه میدارد.
در اولین رندر، اولین useEffect getData را اجرا می کند. بررسی می کند که آیا مرورگر کاربر از web worker ها پشتیبانی می کند یا خیر. همچنین یک شی درخواست را در خود جای می دهد که قبل از ارسال به فایل کارگر getData رشته بندی شده است. شی درخواست شامل یک عمل، نقطه، و جفت کلید-مقدارPageNumber است. useEffect دوم پاسخی را از getData دریافت می کند و پاسخ دریافتی را در حالت profileList قرار می دهد.
در نشانهگذاری، زمانی که فرآیند هنوز در حال اجرا است، یک لودر نمایش میدهیم و پس از اتمام، جدولی را ارائه میکنیم. به خاطر داشته باشید که کلاس های ارسال شده به عناصر برای استایل دادن به آنها استفاده می شود
یک کارگر برای Pagination جدول در TypeScript ایجاد کنید
هنگامی که کاربر دکمه های بعدی و قبلی یا شماره صفحه را انتخاب می کند، کامپوننت pagination به صفحه خاصی هدایت می شود.
برای پیادهسازی worker برای این عملکرد، workData را در longProcesses/getData.ts تغییر میدهیم و کد زیر را به آن اضافه میکنیم:
// longProcesses/getData.ts
if (
data.period === "pageNumber" ||
data.period === "next" ||
data.period === "prev"
) {
const items = profiles.slice(
(data.thePageNumber - 1) * listPageSize,
data.thePageNumber * listPageSize
);
const response = {
loading: false,
list: items,
page: data.thePageNumber,
} as ProfileListType;
self.postMessage(JSON.stringify(response));
}
از فایلی که پیامی را برای کارگر getData ارسال کرده است، یک شی داده حاوی یک کلید نقطه دریافت خواهیم کرد. قبل از برش صفحه با استفاده از شماره صفحه و اندازه صفحه فهرست، بررسی می کنیم که آیا این کلید برابر با pageNumber، next یا prev است. سپس، یک شی پاسخ ایجاد کنید و پاسخ را به عنوان یک پیام رشته ای ارسال کنید.
پیاده سازی کامپوننت Pagination در React
برای ایجاد یک کامپوننت pagination، کد زیر را به components/Pagination اضافه میکنیم و صفحهبندی component.tsx را ایجاد میکنیم:
// Pagination.tsx
import React from "react";
type Props = {
page: number;
pages: number;
pageClick: (page: number) => void;
prevHandler: () => void;
nextHandler: () => void;
};
const Pagination = ({
page,
pages,
pageClick,
prevHandler,
nextHandler,
}: Props) => {
return (
<div className="pagination-container">
<button className="prev" onClick={prevHandler} disabled={page === 1}>
Prev
</button>
<ul className="pages-container">
{[...Array(Math.ceil(pages)).keys()].map((x, i) => {
return (
<li
key={i}
className={page - 1 === i ? "active page-item" : "page-item"}
onClick={() => {
pageClick(x + 1);
}}
>
{x + 1}
</li>
);
})}
</ul>
<button className="next" onClick={nextHandler} disabled={page === pages}>
Next
</button>
</div>
);
};
export default Pagination;
ابتدا نوع props را تعریف می کنیم که شامل page و pages است و باید یک عدد را بپذیرد. pageClick، prevHandler و nextHandler باید یک تابع را بپذیرند. در jsx، یک div را با یک کلاس برمی گردانیم. در داخل div، سه عنصر دکمه، ul و دکمه دوم را برمی گردانیم.
اولین دکمه شامل یک کلاس و یک شنونده رویداد onclick است که تابع prevHandler را اجرا می کند. وقتی صفحه فعلی اولین صفحه باشد، غیرفعال می شود. ul شامل یک کلاس است و آرایه ای از اعداد را با استفاده از مقدار صفحات برمی گرداند.
ما از یک تابع map برای حلقه زدن در آرایه جدید و برگرداندن یک عنصر li استفاده می کنیم. هر li حاوی یک کلید است، کلاسی که با فعال بودن li تغییر می کند. همچنین حاوی یک کنترل کننده رویداد onClick است که تابع pageClick را اجرا می کند.
اگر TypeScript از تبدیل یک عدد به آرایه شکایت دارد، جفت کلید زیر را به شی compilerOption در فایل tsconfig.json خود اضافه کنید:
// tsconfig.json
"downlevelIteration": true /** This fixes error when converting numbers to array of numbers*/,
آخرین دکمه دکمه بعدی است که یک کلاس و یک کنترل کننده رویداد onClick را می پذیرد که تابع nextHandler را اجرا می کند. وقتی صفحه فعلی آخرین صفحه باشد، غیرفعال می شود.
طراحی کنترل کننده صفحه
کنترلکنندههای صفحه، توابعی هستند که هنگام استفاده از آن به کامپپوننت صفحهبندی منتقل میکنیم. اولین تابعی که ایجاد می کنیم handlePageNumber است که وقتی کاربر روی صفحه کلیک می کند فراخوانی می شود:
const handlePageNumber = (userSelectedPage: number) => {
if (window.Worker) {
const request = {
action: processList.getData,
period: "pageNumber",
thePageNumber: userSelectedPage,
} as GetDataType;
getData.postMessage(JSON.stringify(request));
}
};
تابع دوم، prevHandler، با کلیک روی دکمه prev فراخوانی می شود:
const prevHandler = (userSelectedPage: number) => {
if (profileList.page === 1) {
return;
}
if (window.Worker) {
const request = {
action: processList.getData,
period: "prev",
thePageNumber: userSelectedPage - 1,
} as GetDataType;
getData.postMessage(JSON.stringify(request));
}
};
آخرین تابع ما nextHandler است که با کلیک روی دکمه بعدی فراخوانی می شود:
const nextHandler = (userSelectedPage: number, thePageLength: number) => {
if (userSelectedPage < thePageLength) {
if (window.Worker) {
const request = {
action: processList.getData,
period: "next",
thePageNumber: userSelectedPage + 1,
} as GetDataType;
getData.postMessage(JSON.stringify(request));
}
}
};
handlePageNumber userSelectedPage را به عنوان یک پارامتر می پذیرد. سپس بررسی می کنیم که آیا مرورگر web worker ها را پشتیبانی می کند یا خیر. اگر چنین است، یک شی درخواست ایجاد میکنیم که حاوی اکشن، نقطه و thePageNumber است. ما درخواست را به عنوان یک پیام رشته ای برای web worker ها getData ارسال می کنیم.
تابع بعدی ما، prevHandler، userSelectedPage را به عنوان یک پارامتر می پذیرد. بررسی می کنیم که آیا صفحه فعلی صفحه اول است یا خیر و کاری انجام نمی دهیم. سپس، یک شی درخواست ایجاد می کنیم. با این حال، ما یک کلید کاهشیافته PageNumber ارسال میکنیم.
در نهایت، nextHandler مانند دوتابع اول است، با این حال، به جای آن دو ویژگی را می پذیرد. ابتدا بررسی می کنیم که آیا userSelectedPage کمتر از مقدار متغیر PageLength است که در بالا تعریف کردیم. علاوه بر این، کلید thePageNumber افزایش می یابد.
با استفاده از کامپوننت pagination
برای استفاده از کامپوننت pagination، باید آن را به component/App.ts وارد کنیم:
// App.ts
import Pagination from "./components/Pagination";
در مرحله بعد، نشانه گذاری زیر را در زیر کامپوننت جدول اضافه می کنیم:
// App.ts
<Pagination
page={profileList.page}
pages={lengthCount.value / listPageSize}
pageClick={(pageNumber) => {
handlePageNumber(pageNumber);
}}
prevHandler={() => prevHandler(profileList.page)}
nextHandler={() =>
nextHandler(profileList.page, lengthCount.value / listPageSize)
}
/>
ابتدا profileList.page را به صفحه، lengthCount.value / listPageSize را به صفحات و تابع handlePageNumber را به صفحه pageClick میدهیم. در نهایت، prevHandler و nextHandler را به props مناسب خود میدهیم.
اضافه کردن یک استایل طراحی به web worker ها
برای استایل دادن به web worker ها، از کد زیر استفاده می کنم. با این حال، می توانید از هر استایل دلخواه خود استفاده کنید:
// index.css
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-bottom: 4rem;
}
.loader {
animation: spinner 1s linear forwards infinite;
}
@keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.main-container {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 2rem;
}
.count {
padding: 2rem 0 3rem;
font-size: 2rem;
text-align: center;
}
.table-container {
width: 95%;
padding: 0 0.5rem;
max-width: 700px;
}
table,
td,
th {
border: 1px solid black;
padding: 0.2rem;
}
table {
border-collapse: collapse;
width: 100%;
min-height: 700px;
}
th {
white-space: nowrap;
}
td {
text-align: left;
vertical-align: top;
}
td:first-child {
font-weight: 300;
}
.pagination-container {
display: flex;
align-items: center;
justify-content: center;
margin: auto;
gap: 1rem;
width: 100%;
max-width: 500px;
}
.pages-container {
display: flex;
list-style: none;
gap: 0.5rem;
overflow-x: scroll;
padding-bottom: 0.5rem;
}
.pages-container > * {
background-color: rgb(49, 150, 238);
padding: 0.3rem;
min-width: 40px;
text-align: center;
cursor: pointer;
border: none;
border-radius: 8px;
}
.page-item.active {
background-color: blue;
color: white;
}
.prev,
.next {
padding: 0.6rem;
text-align: center;
cursor: pointer;
}
نتیجه
استفاده از web worker ها با TypeScript و React بسیار ساده تر شده است. ما اکنون می توانیم عملیات و وظایف سنگین را در پس زمینه انجام دهیم و در عین حال عملکرد و UX را بهبود بخشیم.
با تشکر برای خواندن! امیدوارم از این مقاله لذت برده باشید و اگر سوالی داشتید حتما نظر بدهید.
