جاوااسکریپت | JavaScript
527 subscribers
704 photos
158 videos
4 files
566 links
کانال @IR_javascript حاوی اطلاعات مفید در حوزه برنامه نویس فرانت که بصورت روزانه بروز می‌شود.
در این کانال شما به:
[1] مطالب تازه
[2] تحلیل‌های عمیق
[3] نکات آموزشی
[4] چالش
[5] ابزار و راهنمایی‌های کاربردی
دسترسی خواهید داشت.

🆔@IR_javascript
Download Telegram
‏**Proxy و Reflect: چطور کار با آبجکت‌ها راحت‌تر می‌شود**

در JavaScript ابزارهای قدرتمندی وجود دارند که گاهی کمتر به چشم می‌آیند. Proxy و Reflect دقیقاً از همین دست ابزارها هستند که اغلب پشت صحنه باقی می‌مانند. اما اگر بدانید چطور از آن‌ها درست استفاده کنید، می‌توانند توسعه را بسیار ساده‌تر کنند. بیایید چند مثال واقعی را ببینیم که در آن‌ها Proxy و Reflect مفید هستند.

💬 لاگ‌گیری و دیباگ

فرض کنید می‌خواهید ردیابی کنید چه کسی و چه زمانی به یک آبجکت دسترسی پیدا می‌کند. به جای اینکه کد را پر از console.log کنید، می‌توانید از Proxy استفاده کنید تا همه دسترسی‌ها را رهگیری کنید. این روش مخصوصاً وقتی با استیت‌های بزرگ مثل Vue یا MobX کار می‌کنید، فوق‌العاده کاربردی است.

const user = { name: "Alex", age: 25 };

const loggedUser = new Proxy(user, {
get(target, prop) {
console.log(`GET ${prop}`);
return Reflect.get(target, prop);
},
set(target, prop, value) {
console.log(`SET ${prop} = ${value}`);
return Reflect.set(target, prop, value);
}
});

loggedUser.name; // GET name
loggedUser.age = 26; // SET age = 26


💬 اعتبارسنجی داده‌ها

Proxy می‌تواند مقادیر نادرست را شناسایی کرده و اجازه ندهد وارد منطق کسب‌وکار شوند. با این روش می‌توان مدل‌های داده ساده بدون کلاس یا کد اضافی ساخت. کنترل و اعتبارسنجی داده‌ها دیگر به این آسانی نبوده است.

const product = new Proxy({}, {
set(target, prop, value) {
if (prop === 'price' && value < 0) {
throw new Error("قیمت نمی‌تواند منفی باشد");
}
return Reflect.set(target, prop, value);
}
});


💬 Reflect: دسترسی ایمن

Reflect مجموعه‌ای از متدها برای کار ایمن با آبجکت‌هاست. این متدها عملیات استاندارد را انجام می‌دهند، اما جلوی خطاهای غیرمنتظره را می‌گیرند و رفتار قابل پیش‌بینی ایجاد می‌کنند.

Reflect.get(obj, 'key'); // به جای obj.key  
Reflect.set(obj, 'key', 'value'); // true/false برمی‌گرداند، خطا نمی‌دهد


وقتی با Proxy ترکیب شود:

const safe = new Proxy(obj, {
get: (t, p) => Reflect.get(t, p),
set: (t, p, v) => Reflect.set(t, p, v)
});


📌 بسیاری از سیستم‌های واکنش‌گرایی مثل Vue 3 و MobX هم بر اساس Proxy ساخته شده‌اند. این ابزار به شما اجازه می‌دهد به تغییر داده‌ها «مشترک شوید» و UI را به‌طور خودکار بروزرسانی کنید، بدون اینکه ساختار داده را تغییر دهید.

Proxy و Reflect ابزارهای قدرتمندی هستند؛ اگر درست استفاده شوند، می‌توانند لایه‌های مؤثر لاگ‌گیری، اعتبارسنجی و واکنش‌گرایی را بدون کد اضافی یا ترفندهای پیچیده بسازند.



#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
‏URLPattern: روشی ساده برای کار با نشانی‌های اینترنتی بدون دردسر 🧐

کار با نشانی‌های اینترنتی معمولاً یعنی این‌که مسیر، پارامترها، میزبان، پروتکل را دستی استخراج کنیم و امید داشته باشیم که یک عبارت باقاعده‌ی جدید نصف برنامه را از کار نیندازد. اما در مرورگرهای مدرن ابزاری داخلی وجود دارد که این فرایند را بسیار قابل‌فهم‌تر می‌کند — URLPattern.

متد exec()
اطلاعاتی دقیق دربارهٔ تطبیق بازمی‌گرداند یا اگر تطبیقی وجود نداشته باشد مقدار null را برمی‌گرداند.
نتیجه شامل ویژگی‌هایی برای هر بخش از نشانی اینترنتی است (protocol، hostname، pathname، search، hash و مانند آن).
هر بخش معمولاً دارای فیلدی به نام input (رشتهٔ واقعی) و groups است که شیئی شامل مقادیر نام‌گذاری‌شدهٔ استخراج‌شده است.

const pattern = new URLPattern({
protocol: 'https',
hostname: 'example.com',
pathname: '/posts/:year/:slug',
search: 'page=:page',
hash: ':section'
});

const url =
'https://example.com/posts/سال دوهزار و بیست و پنج/urlpattern-guide?page=دو#intro';

const result = pattern.exec(url);

// pathname
console.log(result.pathname.input);
// "/posts/سال دوهزار و بیست و پنج/urlpattern-guide"

console.log(result.pathname.groups.year);
// "سال دوهزار و بیست و پنج"

console.log(result.pathname.groups.slug);
// "urlpattern-guide"

// search
console.log(result.search.input);
// "?page=دو"

console.log(result.search.groups.page);
// "دو"

// hostname و protocol
console.log(result.hostname.input);
// "example.com"

console.log(result.protocol.input);
// "https"


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
This media is not supported in your browser
VIEW IN TELEGRAM
می‌توان ظاهر رابط کاربری را با کاهش یا حذف نوارهای اسکرول از طریق ویژگی scrollbar-width تمیزتر کرد.

نمونهٔ اسکرول‌بار باریک:

ul {
scrollbar-width: thin;
}


نمونهٔ اسکرول‌بار مخفی (ناحیه همچنان قابل اسکرول است):

.cards-container {
scrollbar-width: none;
}


این روش برای سایدبارها، جدول‌های داده، پنل‌های چت، داشبوردهایی با اسکرول‌های متعدد، کاروسل‌ها و رابط‌های لمسی مناسب است.

در نهایت، اسکرول همچنان کار می‌کند، اما ظاهر رابط کاربری مرتب‌تر و چشم‌نوازتر می‌شود. 🥺


#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
👍3
This media is not supported in your browser
VIEW IN TELEGRAM
حذف پس‌زمینه در مرورگر
حذف پس‌زمینه را مستقیماً در مرورگر خود انجام دهید؛ این ابزار با WebGPU کار می‌کند و از مدل «آر‌اِم‌بی‌جی نسخهٔ یک‌ممیز‌چهار» از هاجینگ فِیس استفاده می‌کند.

🔗https://github.com/ducan-ne/remove-bg
#️⃣#npm_module
👥@IR_javascript_group
🆔@IR_javascript
1
غیرفعال کردن عملیاتی که با کلیک راست ماوس انجام می‌شود

شاید زمانی لازم باشد که کاربران وب‌سایت شما نتوانند با کلیک راست ماوس منوی زمینهٔ عناصر را فراخوانی کنند. هرچند این کار چندان رایج نیست، اما گاهی چنین نیازی پیش می‌آید.

و در واقع این روش بسیار ساده امکان می‌دهد که قابلیت کلیک راست ماوس را در یک صفحه غیرفعال کنید.

<body oncontextmenu="return false">
<div></div>
</body>



#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
ویدیو دوبله شده در مورد اسولت نسخهٔ پنج (The Complete Svelte 5 Course) [+لینک]

اسولت یک فریم‌ورک همراه با کامپایلر اختصاصی است که هدفش تولید کد کلاینتی فوق‌العاده سبک است؛ و البته فریم‌ورکی نسبتاً پرطرفدار نیز به شمار می‌آید.

«ماتیا از کرواسی» (امضایی که خودِ نویسنده استفاده می‌کند) دوره‌ای بسیار جامع گرد آورده که به شما امکان می‌دهد اسولت نسخهٔ پنج را از پایه بیاموزید. این دوره شامل بیست و هشت فصل، سه ساعت ویدئو و تعداد زیادی دمو است. دوره رایگان است،
کد ها در لینک زیر موحود است
https://joyofcode.xyz/learn-svelte

🔗https://www.aparat.com/v/dbp98sl
#️⃣#tip #dub
👥@IR_javascript_group
🆔@IR_javascript
👍1
This media is not supported in your browser
VIEW IN TELEGRAM
Draw DB

وب‌سایتی است که به‌عنوان یک سامانهٔ تعاملی عمل می‌کند و به کاربران امکان می‌دهد نمودارها و دیاگرام‌های گرافیکی بسازند و با دیگران به‌اشتراک بگذارند، و بدین‌وسیله تصویری‌سازی ایده‌ها و مفاهیم را تسهیل می‌کند.


🔗https://www.drawdb.app
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
This media is not supported in your browser
VIEW IN TELEGRAM
گیم‌شِل — یک بازیِ آموزشیِ کنسولی است که از ماجراجویی‌های کلاسیک متنی الهام گرفته؛ جایی که تمام دنیا در قالب متن وجود دارد و اعمال شما همان فرمان‌ها هستند. تنها تفاوت این است که این‌جا فرمان‌ها واقعیِ یونیکس‌اند. بازی با مأموریت‌ها و وظایف، کار با ترمینال را آموزش می‌دهد و یادگیری را به یک کوئست تبدیل می‌کند.
است، روی لینوکس و مک‌او‌اِس اجرا می‌شود و یک ایمیج داکر نیز دارد. نصب آن حداقلی است: اسکریپت را دانلود می‌کنید، اجرا می‌کنید — و ی
این پروژه در اصل به‌عنوان یک کار دانشگاهی شکل گرفت: نویسنده گیم‌شِل را برای دانشجویانی ساخت که باید به‌سرعت فرمان‌های پایهٔ لینوکس را یاد می‌گرفتند. ایده کاملاً جواب داد — به‌جای شنیدن سخنرانی‌های خسته‌کننده، دانشجویان شروع کردند به انجام مأموریت‌های عملی با استفاده از ابزارهای واقعی خط فرمان.

گیم‌شِل کاملاً متن‌باز ک محیط تمرینی با امکان حفظ پیشرفت دریافت می‌کنید.

هر مأموریت یک «اتاق» متنی کوچک است، درست مانند بازی‌های ماجرایی قدیمی، اما با فرمان‌های واقعی.

کاملاً پیشنهاد می‌شود.

🔗https://github.com/phyver/GameShell/
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
👎1
اگر صفحهٔ خطا از حداقل اندازهٔ مشخصی کوتاه‌تر باشد (معمولاً پانصد و دوازده بایت)، مرورگر آن را پنهان می‌کند و صفحهٔ خطای پیش‌فرض خودش را نمایش می‌دهد؛ همان پیام معروف «این صفحه قابل نمایش نیست».
این مشکل حدود پانزده سال پیش هم مطرح شده است، بدون هیچ پیشرفتی
در واقع، این پُر کردن صفحه با کامنت‌ها را خودِ سرور اِن‌جین‌اِکس انجام می‌دهد.


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
1
vue-wave-player

یک پخش‌کنندهٔ صوتی برای پیام‌های صوتی به سبک تلگرام، ویژهٔ ویو نسخهٔ سه.
موج صدا روی Canvas رندر می‌شود و تنها چهل‌و‌هشت و نیم کیلوبایت حجم دارد. همچنین برای پخش هر نوع فایل صوتی قابل استفاده است.

قابلیت‌ها:
— رندر مبتنی بر Canvas با پشتیبانی از رتینا
— جابه‌جایی روی موج + پشتیبانی از لمس
— طراحی واکنش‌گرا از سیصد پیکسل تا هر عرض دلخواه
— انیمیشن نرم برای ظاهر شدن موج
— مکث خودکار تمام پخش‌کننده‌های صفحه هنگام پخش یکی از آن‌ها
— امکان شخصی‌سازی رنگ‌ها، اندازه‌ها و شبکه

🔗https://vue-wave-player.vercel.app/
#️⃣#npm_module
👥@IR_javascript_group
🆔@IR_javascript
👍32
👏 <template> و <slot>

قهرمانان فراموش‌شدهٔ فرانت‌اند که شایستهٔ توجه هستند

همهٔ ما فریم‌ورک‌ها را می‌شناسیم و دوست داریم، اما باید قبول کنیم که مرورگرها می‌توانند کارهای بسیار بیشتری انجام دهند. دو عنصر که اغلب نادیده گرفته می‌شوند اما می‌توانند زندگی توسعه‌دهندگان را بسیار ساده‌تر کنند، <template> و <slot> هستند. این عناصر امکان ساخت «مینی‌کامپوننت‌ها» را مستقیماً در مرورگر فراهم می‌کنند، بدون نیاز به React، Vue یا Svelte.

❗️ <template>

عنصر <template> برای ذخیرهٔ بخش‌هایی از کد HTML استفاده می‌شود که تا زمانی که با جاوااسکریپت به صورت دستی درج نشوند، در صفحه نمایش داده نمی‌شوند. این ابزار ایده‌آل برای ایجاد بلوک‌های تکرارشوندهٔ HTML است.

<template id="card">
<div class="card">
<h3 class="title"></h3>
<p class="text"></p>
</div>
</template>

<script>
const tpl = document.querySelector('#card');
const clone = tpl.content.cloneNode(true);
clone.querySelector('.title').textContent = 'Hello';
document.body.appendChild(clone);
</script>


❗️ <slot>

عنصر <slot> درون کامپوننت‌های سفارشی برای درج HTML خارجی استفاده می‌شود، که اجازه می‌دهد کامپوننت‌های رابط کاربری کامل بدون فریم‌ورک‌های خارجی ایجاد شوند. این روش هم معنایی و دسترس‌پذیر است و هم سفارشی‌سازی کامپوننت‌ها را ساده می‌کند.

<my-button>
<span>Click me</span>
</my-button>

<script>
class MyButton extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<button class="btn">
<slot></slot>
</button>
`;
}
}
customElements.define('my-button', MyButton);
</script>


📌 مرورگرها اکنون آن‌قدر هوشمند شده‌اند که حتی فریم‌ورک‌های پیچیده هم لازم نیستند! همهٔ این‌ها به لطف Web Components، Islands، Partial Hydration و SSR امکان‌پذیر است. و استفاده از این دو عنصر <template> و <slot> باعث می‌شود بسیاری از مسائل که قبلاً تنها با فریم‌ورک‌های کوچک حل می‌شدند، حالا بدون بار اضافی و پیچیدگی قابل مدیریت باشند.

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
2
ویژگی‌های منطقی در CSS — چگونه استایل‌ها را منعطف و جهانی کنیم

همیشه به حاشیه‌ها از بالا، راست، پایین و چپ فکر می‌کنیم. اما در دنیایی که جهت متن می‌تواند تغییر کند، این روش محدودکننده می‌شود. اینجا logical properties به کمک می‌آیند. آن‌ها اجازه می‌دهند حاشیه‌ها و اندازه‌ها را بر اساس جریان متن تعیین کنید، نه بر اساس سمت‌ها.

ℹ️ logical properties چیست و چه کاربردی دارند؟

inline جهت متن را مشخص می‌کند (مثلاً از چپ به راست یا راست به چپ)

block جهت بلوک‌ها را تعیین می‌کند، معمولاً از بالا به پایین، بسته به جریان سند

برای حاشیه‌های افقی از margin-inline-start و margin-inline-end استفاده می‌شود

برای حاشیه‌های عمودی از padding-block-start و padding-block-end

برای موقعیت و مرزها از border-inline و inset-block استفاده می‌شود

مزایا و کاربردها:

بین‌المللی‌سازی
در محصولاتی که چند زبان پشتیبانی می‌کنند، logical properties باعث صرفه‌جویی در زمان تنظیم استایل‌ها می‌شود.

طرح‌های قابل تغییر جهت
برای کامپوننت‌هایی که ممکن است «برگردانده» شوند (مثل آواتار سمت چپ/راست یا سایدبار)، logical properties نیاز به شرط‌نویسی در CSS را حذف می‌کند.

/* روش قدیمی */
.card {
padding-top: 12px;
padding-left: 16px;
}

/* با ویژگی‌های منطقی */
.card {
padding-block-start: 12px;
padding-inline-start: 16px;
}

/* خلاصهٔ کوتاه:
margin-inline — حاشیه‌های افقی
margin-block — حاشیه‌های عمودی
padding-inline — فاصله داخلی افقی
padding-block — فاصله داخلی عمودی
inset-inline — موقعیت افقی (چپ/راست)
inset-block — موقعیت عمودی (بالا/پایین) */




#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
📸 ویژگی‌های loading`، `decoding و fetchpriority — چگونه با ویژگی‌های بومی، بارگذاری تصاویر را سریع‌تر کنیم

سه ویژگی ساده و بومی HTML وجود دارند که می‌توانند بارگذاری تصاویر را بسیار سریع‌تر کرده و رابط کاربری شما را پاسخگوتر کنند. اگرچه تمام مرورگرهای مدرن از آن‌ها پشتیبانی می‌کنند، اما در عمل به ندرت استفاده می‌شوند. در ادامه هر یک را بررسی می‌کنیم:

⬅️ `loading="lazy"` — بارگذاری تنبل

ویژگی loading="lazy" باعث می‌شود مرورگر بارگذاری تصاویر خارج از دید کاربر را به تعویق بیندازد. این کار ترافیک کاربر را کاهش می‌دهد، زمان تا تعامل کامل (TTI) را کم می‌کند و فشار روی دستگاه‌های ضعیف را کاهش می‌دهد. از این ویژگی برای تمام تصاویر زیر اولین صفحه، کارت‌ها، فهرست‌ها و گالری‌ها استفاده کنید.
⚠️ اما برای تصاویر مهم در بالای صفحه، مانند لوگو یا تصاویر Hero، از این ویژگی استفاده نکنید تا از تأخیر در بارگذاری جلوگیری شود.

⚡️ `decoding="async"` — رمزگشایی غیرهمزمان

ویژگی async به مرورگر اجازه می‌دهد تصاویر را به صورت غیرهمزمان رمزگشایی کند**، بدون اینکه رندر سایر عناصر را متوقف کند. این کار باعث کاهش تأخیرهای بصری و افزایش سرعت بارگذاری صفحات می‌شود. به‌ویژه برای تصاویر بزرگ و عناصر در فیدها یا لیست‌ها مفید است و تجربهٔ کاربری روان‌تر را فراهم می‌کند.

🔫
fetchpriority="high | low" — مدیریت اولویت بارگذاری**

این ویژگی تعیین می‌کند که تصویر با چه اولویتی بارگذاری شود: high برای بارگذاری فوری و low برای بارگذاری با تأخیر. استفاده از آن باعث کاهش رقابت منابع، افزایش سرعت LCP و کاهش تأخیرهای بصری در رندر صفحه می‌شود.

👀 نمونه‌ها و نحوهٔ استفادهٔ ترکیبی

<!-- بارگذاری تنبل -->
<img src="hero.png" loading="lazy" />

<!-- رمزگشایی غیرهمزمان -->
<img src="photo.jpg" decoding="async" />

<!-- مدیریت اولویت بارگذاری -->
<img src="hero.jpg" fetchpriority="high" />
<img src="avatar.jpg" loading="lazy" fetchpriority="low" />

<!-- ترکیب همه ویژگی‌ها -->
<img src="hero.jpg" decoding="async" fetchpriority="high" />
<img src="thumb.jpg" loading="lazy" decoding="async" fetchpriority="low" />


استفاده از این سه ویژگی به‌صورت هوشمندانه می‌تواند سرعت بارگذاری تصاویر و تجربهٔ کاربری سایت شما را به طور قابل توجهی بهبود دهد.


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍2
📣 همگام‌سازی وضعیت بین تب‌ها: BroadcastChannel، SharedWorker و localStorage

وقتی کاربر چند تب از یک اپلیکیشن را باز می‌کند، اغلب مشکل همگام‌سازی وضعیت پیش می‌آید. مثلاً کاربر در یک تب از حساب کاربری خارج می‌شود، اما در تب‌های دیگر اپلیکیشن همچنان او را وارد شده فرض می‌کند. در سال ۲۰۲۵ چند ابزار برای حل این مسئله داریم: BroadcastChannel**، **SharedWorker و برای مرورگرهای قدیمی، localStorage.

---

BroadcastChannel — راه‌حل ساده برای همگام‌سازی پیام‌ها

این API امکان ارسال و دریافت پیام‌ها بین تب‌ها را به صورت همزمان فراهم می‌کند. مناسب برای:

* اطلاع‌رسانی ورود/خروج
* انتخاب تم
* به‌روزرسانی کش
* اطلاع از نسخهٔ جدید اپلیکیشن

مزایا: ساده، سریع، کار در پس‌زمینه
محدودیت‌ها: وضعیت مشترک پیچیده را پشتیبانی نمی‌کند

// BroadcastChannel
const channel = new BroadcastChannel("session");

channel.onmessage = (e) => {
console.log("message:", e.data);
};

channel.postMessage({ loggedOut: true });


---

SharedWorker — وقتی همه تب‌ها مثل یک اپلیکیشن عمل می‌کنند

ابزاری برای کار متمرکز بین تب‌ها که امکان همگام‌سازی وضعیت، کشینگ و مدیریت درخواست‌ها را فراهم می‌کند. مناسب برای اپلیکیشن‌هایی که نیاز به هماهنگی بین تب‌ها دارند.

مزایا: ذخیرهٔ مرکزی وضعیت، اشتراک منابع، کاهش درخواست‌های تکراری API
محدودیت‌ها: در Safari پشتیبانی نمی‌شود، دیباگ پیچیده و نیاز به معماری حساب‌شده دارد

// main.js
const worker = new SharedWorker("worker.js");
worker.port.postMessage({ type: "ping" });
worker.port.onmessage = (e) => console.log(e.data);

// worker.js
onconnect = (e) => {
const port = e.ports[0];
port.onmessage = () => port.postMessage("pong");
};


---

LocalStorage + storage event — روش قدیمی ولی کارآمد

یکی از ساده‌ترین روش‌های همگام‌سازی داده‌ها، مناسب برای:

* مرورگرهای قدیمی
* پروژه‌هایی بدون ServiceWorker یا SharedWorker
* سناریوهای سادهٔ همگام‌سازی تب‌ها

مزایا: پیاده‌سازی ساده، سازگاری بالا
محدودیت‌ها: فقط رشته‌ها ذخیره می‌شوند، رویداد storage در تب تغییر دهنده رخ نمی‌دهد و ممکن است کندتر از BroadcastChannel باشد

// LocalStorage + storage event
window.addEventListener("storage", (e) => {
if (e.key === "logout") {
// همگام‌سازی وضعیت
}
});


---

📌 جمع‌بندی:

* برای اعلان‌ها و رویدادهای ورود/خروج → BroadcastChannel
* برای اشتراک وضعیت در PWA و داشبوردها → SharedWorker
* برای مرورگرهای قدیمی → localStorage + storage event

هر ابزار ویژگی‌های خاص خود را دارد و انتخاب آن بستگی به پیچیدگی پروژه، مرورگرهای هدف و نوع داده‌ها دارد.


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1
Media is too big
VIEW IN TELEGRAM
کش کردن ETag در HTTP یک مکانیزم برای اعتبارسنجی کش وب است.
در این ویدئو، خواهیم آموخت که ETag‌ها چه هستند و چگونه با هدرهای If-None-Match و If-Match و کدهای وضعیت HTTP ۳۰۴ و ۴۱۲ مرتبط می‌شوند.


🔗https://www.youtube.com/watch?v=tWu9lBlghOc
#️⃣#tip #dub
👥@IR_javascript_group
🆔@IR_javascript
🎭 چگونه یک API فرانت‌اند بدون دردسر بنویسیم

هنگام توسعهٔ API فرانت‌اند، مهم است که به یاد داشته باشیم این فقط یک لایهٔ نازک روی بک‌اند نیست. این یک قرارداد کامل است که مشخص می‌کند محصول چقدر کاربرپسند خواهد بود، تیم چگونه آن را درک می‌کند و چه مشکلاتی در آینده ممکن است رخ دهد. در ادامه یک چک‌لیست برای ساخت API ارائه می‌شود که نه‌تنها کار می‌کند، بلکه به‌راحتی قابل نگهداری است.

⬆️ پیش‌بینی‌پذیری API

یکی از بزرگ‌ترین مشکلات، عدم انسجام است. نام‌های متفاوت فیلدها، ساختارهای مختلف پاسخ یا کدهای خطا می‌توانند هر تلاش برای کار پایدار با API را خراب کنند. اگر توسعه‌دهنده با یکی از اندپوینت‌های شما کار کرده باشد، باید اطمینان داشته باشد که اندپوینت بعدی تقریباً مشابه خواهد بود. این موضوع به جلوگیری از مشکلات نگهداری و پیاده‌سازی سریع ویژگی‌های جدید کمک می‌کند.

⬆️ مستندسازی خودکار

مستندسازی دستی به‌سرعت منسوخ می‌شود. از ابزارهایی استفاده کنید که مستندسازی را به‌صورت خودکار تولید کنند. از OpenAPI یا Swagger، JSON Schema و تولید خودکار تایپ‌های TypeScript و کلاینت استفاده کنید. با این روش، API و مستندات همیشه به‌روز خواهند بود و فرانت‌اند پایدارتر خواهد شد.

⬆️ ثبات نوع داده‌ها

مشکلات زمانی آغاز می‌شوند که فیلدهای داده‌ای هم می‌توانند null باشند و هم ممکن است اصلاً وجود نداشته باشند. این موضوع رابط کاربری را خراب کرده و اپلیکیشن را ناپایدار می‌کند. برای مثال، بهتر است همیشه ساختار یکسانی با مقادیر صحیح داشته باشیم تا فیلدهای تصادفی با انواع دادهٔ متفاوت. اطمینان حاصل کنید که داده‌ها همیشه پایدار و پیش‌بینی‌پذیر باشند.

⬆️ از «مقدارهای جادویی» پرهیز کنید

نمونهٔ بد یک API: { "status": 3 }. این مقدار چه معنایی دارد؟ این قابل خواندن نیست. بهتر است از مقادیر قابل فهم استفاده کنید، مثل { "status": "blocked" }. این کار تعداد خطاها را کاهش می‌دهد، نگهداری و خواندن API را آسان‌تر می‌کند و فهم توسعه‌دهندگان را افزایش می‌دهد.

⬆️ خطاها فقط کد وضعیت نیستند

خطاها باید با دقت مدیریت شوند. کدهای خطای پایه مانند ۲۰۰ و ۵۰۰ برای عملکرد خوب کافی نیستند. به جای آن از کدهای خطای واضح همراه با توضیح استفاده کنید. برای مثال، اگر جلسه منقضی شده باشد، پاسخ دهید:

{
"error": {
"code": "INVALID_TOKEN",
"message": "Token expired"
}
}


این کار به بازیابی مؤثر جلسات، retry و بهبود تجربهٔ کاربری کمک می‌کند.

⬆️ هنگام نوشتن API به نیازهای فرانت‌اند توجه کنید:

• کاهش تعداد درخواست‌های کوچک
• قابلیت کش کردن
• داده‌های نرمال‌شده
• پاسخ‌های فشرده

فرانت‌اند دوست ندارد API پاسخ‌های حجیم با داده‌های اضافی بازگرداند. به صفحه‌بندی، batch کردن و ETag‌های پایدار برای منابع توجه کنید.

⬆️ همه چیز مورد نیاز برای کش کردن را فراهم کنید

‏API باید از کش پشتیبانی کند. از مکانیزم‌هایی مانند ETag**، **Cache-Control**، **Last-Modified و max-age استفاده کنید. این امکان را به فرانت‌اند می‌دهد که کش را به‌صورت مؤثر استفاده کرده، بار را کاهش دهد و سرعت را افزایش دهد.

⬆️ از over-fetching و under-fetching محافظت کنید

اطمینان حاصل کنید که API داده‌های اضافی (over-fetching) ارسال نمی‌کند، مثل زمانی که نیازی به نقش‌ها یا تنظیمات کاربران نیست. همچنین فرانت‌اند را مجبور نکنید چندین درخواست برای دریافت داده‌های مورد نیاز ارسال کند (under-fetching). از view model‌هایی استفاده کنید که برای صفحات خاص UI طراحی شده‌اند.

⬆️ نسخه‌بندی API را با دقت انجام دهید

نسخه‌بندی API باید هدفمند باشد. مسیر ساده با شماره نسخه‌ها مانند /v2 یا /v3 کافی نیست. اطمینان حاصل کنید که هر نسخه داخلی سازگار است و امکان بازگشت فراهم باشد. نسخه‌بندی باید پیش‌بینی‌پذیر و واضح باشد.

⬆️ یک لایۀ کلاینت ایجاد کنید

فرانت‌اند باید بتواند کارهایی مانند retry، لغو درخواست‌ها، debounce، timeout و مدیریت توکن را به‌صورت مستقل انجام دهد. این‌ها نباید مسئولیت بک‌اند باشند. یک لایۀ واحد API برای کلاینت با استفاده از custom fetch wrapper، axios instance یا TanStack Query ایجاد کنید. یک کلاس واحد برای همهٔ درخواست‌ها، رفتار پایدارتر و کنترل بهتر روی درخواست‌ها را تضمین می‌کند.

📌 یک API فرانت‌اند عالی فقط مجموعه‌ای از نقاط ورود نیست، بلکه یک سیستم کامل است که باید پیش‌بینی‌پذیر و پایدار باشد. بیایید APIهایی بسازیم که فرانت‌اند را خراب نکند و زندگی کل تیم را ساده‌تر کند.

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
2
چطور رفتار برنامه را وقتی یک درخواست مشخص شکست می‌خورد بررسی کنیم؟ 🥲

وقتی تست می‌کنیم، معمولاً در مرورگر حالت «بدون اینترنت» را فعال می‌کنیم. این کار مفید است، اما همیشه واقع‌بینانه نیست؛ در دنیای واقعی معمولاً کل اتصال قطع نمی‌شود، بلکه فقط یک درخواست مشخص به API با خطا مواجه می‌شود. به همین دلیل، گاهی لازم است ببینیم برنامه دقیقاً در چنین شرایطی چگونه رفتار می‌کند. 😁

اینجاست که DevTools به کمک ما می‌آید. در Chrome می‌توان نه‌تنها کل شبکه را قطع کرد، بلکه یک درخواست مشخص را هم مسدود نمود:

✔️ DevTools را باز کنید → تب Network
✔️ درخواست موردنظر را پیدا کنید، روی آن راست‌کلیک کنید → Block request URL / مسدود کردن URL درخواست
✔️ صفحه را رفرش کنید یا عمل مربوطه را تکرار کنید و ببینید برنامه دقیقاً به شکست همین درخواست چگونه واکنش نشان می‌دهد

با این روش می‌توانید موارد زیر را تست کنید:

⏺️ سناریوهای fallback، وقتی بخشی از داده‌ها در دسترس نیست
⏺️ مدیریت خطاها و پیام‌ها یا نوتیفیکیشن‌هایی که به کاربر نمایش داده می‌شود
⏺️ وضعیت لودینگ و اسپینرها، تا مطمئن شوید رابط کاربری در مواجهه با مشکل شبکه رفتار درستی دارد

این کار به‌ویژه زمانی مفید است که سناریوی پیچیده‌ای دارید که به چند API وابسته است؛ می‌توانید چند درخواست را به‌صورت انتخابی مسدود کنید و خرابی‌های جزئی را شبیه‌سازی نمایید. چیزی شبیه یک استرس‌تست هدفمند برای سناریوهای خاص، بدون اینکه لازم باشد اتصال همهٔ سرویس‌ها را به‌طور کامل قطع کنید.

📌 یک عادت کوچک که می‌تواند کیفیت محصول را بالا ببرد:
به‌جای اینکه فقط حالت «آفلاین» را تست کنید، شکست خوردن درخواست‌های تکی را هم بررسی کنید — این حالت به مشکلات واقعی‌ای که کاربر با آن‌ها مواجه می‌شود بسیار نزدیک‌تر است.


#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
👍1
Cumulative Layout Shift:
متریکی است که نشان می‌دهد عناصر صفحه پس از اولین رندر، با چه فرکانسی و تا چه حد به‌صورت غیرمنتظره جابه‌جا می‌شوند. مرورگر برای هر جابه‌جایی محاسبه می‌کند چه بخشی از صفحه تحت تأثیر قرار گرفته و عناصر چه مسافتی حرکت کرده‌اند، سپس این مقادیر را با هم جمع می‌زند. در نتیجه، CLS نه سرعت بارگذاری، بلکه پایداری رابط کاربری در طول زمان را اندازه‌گیری می‌کند.

📚 از کجا می‌آید؟
اگر در لحظهٔ اولین محاسبهٔ layout، مرورگر نداند یک عنصر چه فضایی را اشغال خواهد کرد، برای آن فضایی رزرو نمی‌کند. وقتی آن عنصر ظاهر می‌شود یا اندازه‌اش تغییر می‌کند، مرورگر مجبور است چیدمان صفحه را دوباره محاسبه کند و کاربر نتیجه را به‌صورت یک «پرش» می‌بیند.
تصاویر: اصلی‌ترین و ساده‌ترین مورد
وقتی یک <img> بدون ابعاد از پیش تعیین‌شده بارگذاری می‌شود، مرورگر نمی‌تواند ارتفاع آن را محاسبه کند. وقتی تصویر ظاهر می‌شود، تمام محتوای زیر آن تغییر مکان می‌دهد.
راه حل: از قبل به مرورگر اطلاع دهید - عرض و ارتفاع واقعی را مشخص کنید.
آنها اندازه ذاتی (n نسبت ابعاد) را تنظیم می‌کنند
<img
src="/img/hero.jpg"
width="1200"
height="675"
alt=""
/>

همه تصاویر در تگ‌های <img> قرار نمی‌گیرند. تصاویر پس‌زمینه، کارت‌ها و اجزای پویا اغلب از طریق CSS یا جاوا اسکریپت می‌آیند. در این موارد، عرض، ارتفاع و سایر ویژگی‌ها نقش دارند.
نسبت ابعاد را در نظر می‌گیرد.
.media {
}
aspect-ratio: 16/9;
width: 100%;


فونت‌ها: منبع نادیده گرفته شده‌ی تغییرات
حتی با تصاویر بی‌نقص، CLS می‌تواند به دلیل فونت‌ها باقی بماند. وقتی صفحه لود می‌شود:
۱. مرورگر هنوز فونت اصلی سایت را دانلود نکرده
۲. باید تصمیم بگیرد:
متن را نشان بدهد یا
صبر کند تا فونت دانلود شود؟
اگر بعداً فونت اصلی برسد و جای فونت موقت (fallback) را بگیرد، اندازه‌ی حروف و فاصله‌ی خطوط تغییر می‌کند چیدمان صفحه می‌پرد (CLS)
چه چیزهایی کمک می‌کند:
1-استفاده از swap

font-display: swap;

// متن را فوراً نشان بده
// اول با یک فونت موقت (fallback)
// وقتی فونت اصلی آمد، آن را جایگزین کن

2-استفاده از فونت‌های fallback با متریک‌های نزدیک به فونت اصلی
3-پری لود کردن فونت‌ها فقط در موارد واقعاً بحرانی و ضروری
@font-face {
font-family: MyFont;
src: url(myfont.woff2) format("woff2");
font-display: swap;
}


تبلیغات، iframe و ویجت‌ها

بلوک‌های تبلیغاتی و iframeها معمولاً به‌صورت ناهمگام بارگذاری می‌شوند و بعد از اولین رندر ظاهر می‌گردند. اگر از قبل فضایی برای آن‌ها در نظر گرفته نشده باشد، با ورودشان محتوا را به پایین «هل می‌دهند» و باعث جابه‌جایی چیدمان می‌شوند.

قاعدهٔ کاربردی بسیار ساده است:
هر بلوک خارجی یا ناهمگام باید از پیش یک اسلات مشخص داشته باشد؛
چه با ارتفاع ثابت، چه با aspect-ratio، یا با استفاده از placeholder / skeleton.


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
‏tree-shaking در فونت‌ها

همان‌طور که فایل‌های بزرگ JavaScript به چند فایل کوچک‌تر تقسیم می‌شوند و فقط در صورت نیاز بارگذاری می‌گردند، فایل‌های فونت هم می‌توانند به زیرمجموعه‌های کوچک‌تر شکسته شوند. به این کار font subsetting یا «تقسیم فونت به زیرمجموعه‌ها» گفته می‌شود.

تعداد گلیف‌ها در یک فونت به زبان‌هایی بستگی دارد که آن فونت پشتیبانی می‌کند. بعضی فونت‌ها تقریباً تمام کاراکترهای یونیکد را در خود دارند، در حالی که فونت‌هایی که فقط زبان انگلیسی را پوشش می‌دهند، مجموعهٔ بسیار محدودتری از نمادها را شامل می‌شوند. هرچه تعداد گلیف‌ها بیشتر باشد، حجم فایل فونت هم بزرگ‌تر خواهد بود.

در سایت‌های فارسی‌زبان (و روسی‌زبان)، معمولاً فقط از حروف سیریلیک یا فارسی، اعداد و نشانه‌های اصلی نگارشی استفاده می‌شود. در زبان‌هایی مثل اوکراینی، بلاروسی و سایر زبان‌های اسلاوی، علاوه بر حروف اصلی سیریلیک، تعدادی نماد از «سیریلیک توسعه‌یافته» هم به کار می‌رود.

کاراکترهای لاتین معمولاً استفادهٔ بسیار محدودی دارند؛ چه برسد به نمادهای گروه CJK (چینی، ژاپنی، کره‌ای) یا سایر زبان‌ها. این دقیقاً همان مشکلی است که در مورد کد JavaScript استفاده‌نشده داریم و با code splitting و tree-shaking حل می‌شود. برای فونت‌ها هم می‌توان همین رویکرد را به کار گرفت.

به‌جای یک فونت بزرگ با مجموعه‌ای عظیم از نمادها، می‌توان آن را به چند فونت کوچک‌تر تقسیم کرد:

* فونت سیریلیک پایه: حروف اصلی سیریلیک، اعداد، نشانه‌های رایج نگارشی؛ می‌توان نقل‌قول‌های تایپوگرافیک، خط تیرهٔ بلند، سه‌نقطه و موارد مشابه را هم اضافه کرد؛
* فونت سیریلیک توسعه‌یافته: حروف و نمادهای خاص (علامت‌های دیاکریتیک) برای زبان‌های اوکراینی، بلاروسی، بلغاری، صربی و دیگر زبان‌ها؛
* فونت لاتین پایه: حروف اصلی الفبای لاتین؛
* فونت لاتین توسعه‌یافته: حروف و نمادهای خاص (دیاکریتیک‌ها) برای زبان‌هایی مثل آلمانی، لهستانی، لیتوانیایی، ترکی و غیره؛
* سایر زیرمجموعه‌ها در صورت نیاز.

در یک سایت فارسی یا روسی‌زبان، حروف لاتین معمولاً برای اصطلاحات، نام‌ها و ابزارهای ترجمه لازم می‌شوند. خود من سایت‌های انگلیسی‌زبان را می‌بینم و گاهی از قابلیت ترجمهٔ صفحه استفاده می‌کنم؛ خارجی‌ها هم همین کار را می‌کنند. خوشایند نیست که به‌جای فونت طراحی‌شدهٔ سایت، ناگهان Times New Roman نمایش داده شود.

---

## روش‌های تقسیم فونت

چند راه برای این کار وجود دارد. ساده‌ترین روش استفاده از Google Fonts است؛
نه به این معنا که فونت را مستقیم از آن سرویس لود کنید (که توصیه نمی‌شود)، بلکه برای گرفتن زیرمجموعه‌های آماده:

۱. فونت موردنظر را در Google Fonts پیدا می‌کنید؛
۲. لینک تولیدشده داخل <link rel="stylesheet"> را کپی می‌کنید؛
۳. لینک را در یک تب جدید باز می‌کنید؛ مجموعه‌ای از دستورهای @font-face می‌بینید؛
۴. زیرمجموعه‌های موردنیاز (که با کامنت مشخص شده‌اند) را کپی می‌کنید؛
۵. فایل‌های فونت مربوطه را از آدرس‌های src دانلود می‌کنید؛
۶. دستورهای @font-face را به پروژه اضافه کرده و لینک‌ها را به فایل‌های دانلودشده تغییر می‌دهید.

روش دیگر استفاده از ابزار آنلاین Font Subsetting است. در این روش، فونت را آپلود می‌کنید، نمادها یا بازه‌های یونیکد موردنظر را مشخص می‌کنید و خروجیِ فونتی را می‌گیرید که فقط شامل همان کاراکترهاست.

روش سوم استفاده از ابزار خط فرمان glyphhanger است که جزئیات آن در مقالهٔ زک لِدِرمن توضیح داده شده است. (https://www.zachleat.com/web/glyphhanger/)

---

## نقش unicode-range

برای اینکه مرورگر بداند چه زمانی و کدام زیرمجموعهٔ فونت را بارگذاری کند، باید بداند هر فونت شامل چه کاراکترهایی است. این اطلاعات با ویژگی unicode-range در دستور @font-face مشخص می‌شود:

/* cyrillic */
@font-face {
/* ... */
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}


ویژگی unicode-range فهرستی از کاراکترها را در قالب یونیکد مشخص می‌کند. می‌توان:

* یک نماد مشخص را تعیین کرد (مثلاً U+2116 که همان علامت شماره «№» است)،
* یا یک بازه را مشخص نمود (مثلاً U+0410-044F از حرف «А» تا «я»).

فهرست کامل مقادیر یونیکد را می‌توان در جدول‌های یونیکد مشاهده کرد.

هنگام بارگذاری صفحه، مرورگر تمام کاراکترهای موجود را بررسی می‌کند. اگر حتی یک کاراکتر از عناصر استفاده‌کنندهٔ فونت، داخل بازهٔ unicode-range باشد، آن فونت دانلود می‌شود. اگر هیچ‌کدام از کاراکترهای صفحه در آن بازه نباشند، فونت اصلاً بارگذاری نخواهد شد.


#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
👏 رویکردی نوین برای کار با تم‌ها در CSS با استفاده از ‎@property

پیش‌تر توسعه‌دهندگان ناچار بودند با جاوااسکریپت و استایل‌ها مستقیماً در کد سروکله بزنند. اما اکنون مرورگرها از ‎@property‎ پشتیبانی می‌کنند؛ قابلیتی که امکان ساخت متغیرهای CSS با نوع مشخص، مقدار پیش‌فرض و حتی انیمیشن را فراهم می‌کند.

📝 نمونه‌های پیاده‌سازی:

/* @property چیست؟ */
@property --theme-hue {
syntax: "<number>";
inherits: true;
initial-value: 120;
}

/* تم‌دهی بدون JavaScript */
@property --bg {
syntax: "<color>";
inherits: true;
initial-value: #fff;
}

:root {
--bg: #fff;
--text: #000;
}

[data-theme="dark"] {
--bg: #000;
--text: #fff;
}

body {
background: var(--bg);
color: var(--text);
transition: background .3s, color .3s;
}

/* متغیرهای قابل انیمیشن */
@property --hue {
syntax: "<number>";
inherits: true;
initial-value: 200;
}

body {
background: hsl(var(--hue) 80% 50%);
transition: --hue 0.4s ease;
}

body.dark {
--hue: 320;
}

/* تم‌دهی پویا بدون بازنویسی CSS
با @property می‌توان به‌راحتی مجموعه‌ای از متغیرها را برای تغییر پویای تم‌ها مدیریت کرد */
@property --radius {
syntax: "<length>";
initial-value: 4px;
}

.card {
border-radius: var(--radius);
}

/* اکنون می‌توان میزان گردی گوشه‌ها را در لحظه و با کلاس‌های ساده تغییر داد */
:root.compact {
--radius: 2px;
}

:root.rounded {
--radius: 12px;
}


❗️@property چیست؟

‎CSS @property‎ روشی است برای تعریف متغیرهای CSS به‌عنوان یک ویژگی کامل. این قابلیت امکان تعیین نوع مشخص، مقدار پیش‌فرض و حتی اعمال انیمیشن را فراهم می‌کند. با این رویکرد، متغیر نه‌تنها تایپ‌دار می‌شود، بلکه به‌درستی به ارث می‌رسد، بخشی از منطق انیمیشن‌پذیر می‌شود و در صورت دریافت مقادیر نادرست دچار اختلال نخواهد شد.

— تم‌دهی بدون JavaScript: با استفاده از ‎@property‎ می‌توان تم‌ها را بدون استفاده از جاوااسکریپت تغییر داد. کافی است از ویژگی ‎data-theme="dark"‎ در تگ ‎html‎ استفاده شود تا همه‌چیز به‌صورت نیتیو انیمیت شود.

— متغیرهای قابل انیمیشن: پیش از این، متغیرهای CSS از انیمیشن پشتیبانی نمی‌کردند، اما با ‎@property‎ حتی تم‌های پیچیده نیز قابل انیمیشن هستند. اکنون می‌توان ‎transition‎ را مستقیماً روی متغیر اعمال کرد تا تغییرات به‌صورت روان انجام شود و ساخت تم‌های پویا ساده‌تر گردد.

— تایپ‌گذاری و باگ‌های کمتر: یکی از مشکلات اصلی پیش از ‎@property‎ این بود که مرورگر مقادیر نادرست را بی‌سروصدا می‌پذیرفت. برای مثال ‎--hue: red;‎. بدون ‎@property‎ این مقدار صرفاً نادیده گرفته می‌شد. اما با ‎@property‎، در صورت نامعتبر بودن مقدار، ویژگی به مقدار پیش‌فرض خود بازمی‌گردد.

— دیزاین توکن‌های نیتیو و کامل: با ‎@property‎ می‌توان توکن‌هایی برای جنبه‌های مختلف طراحی مانند رنگ‌ها، فاصله‌ها، مقیاس‌بندی، انیمیشن‌ها و تایپوگرافی ایجاد کرد؛ آن هم بدون نیاز به ابزارهای بیلد.

📌 در سال دوهزار و بیست و پنج، پشتیبانی مرورگرها از ‎@property‎ بسیار مطلوب است و مرورگرهایی مانند Chrome، Edge، Safari و Firefox آن را پشتیبانی می‌کنند؛ بنابراین می‌توان با خیال راحت از این قابلیت در محیط پروداکشن استفاده کرد. اگر هنوز از روش‌های قدیمی همراه با جاوااسکریپت زیاد و راه‌حل‌های موقتی استفاده می‌کنید، وقت آن رسیده که رویکرد خود را به‌روز کنید.


#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
1👍1