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

🆔@IR_javascript
Download Telegram
ساخته‌شده توسط هوش مصنوعی با Three.js


کدنویسی وایب (Vibe Coding یا Vibecoding) یک تکنیک برنامه‌نویسی وابسته به هوش مصنوعی است که در آن، فرد مسئله را در چند جمله به‌عنوان یک پرومت برای یک مدل زبانی بزرگ (LLM) آموزش‌دیده در زمینه‌ی کدنویسی توصیف می‌کند. سپس، LLM کد نرم‌افزار را تولید می‌کند و نقش برنامه‌نویس از کدنویسی دستی به هدایت، آزمایش و اصلاح کد تولیدشده توسط هوش مصنوعی تغییر می‌یابد.

حامیان این روش معتقدند که Vibe Coding به برنامه‌نویسان آماتور اجازه می‌دهد بدون نیاز به آموزش و مهارت‌های عمیق مهندسی نرم‌افزار، نرم‌افزار تولید کنند.

🎮 هم‌اکنون بازی کنید:
summer-afternoon.vlucendo.com
🔗https://t.me/HamidrezaKaramianOfficial/2254
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
👍2
معرفی Motion برای Vue

‏Motion یک کتابخانه‌ی محبوب و قدرتمند برای انیمیشن است که بیشتر با React شناخته می‌شود، اما اکنون نسخه‌ی Vue آن نیز عرضه شده و کاملاً مجهز به تمام ویژگی‌ها است.

🔗https://motion.dev/docs/vue
#️⃣#npm_module
👥@IR_javascript_group
🆔@IR_javascript
👍3
اگر یک آرایه دارید که هر عنصر آن شامل یک مقدار computed است، بهتر است یک مقدار computed برای کل آرایه ایجاد کنید تا اینکه برای هر عنصر جداگانه یک مقدار computed داشته باشید.

### نمونه نامناسب
const rows = productRows.map(row => ({ 
...row,
total: computed(() => row.price * row.qty),
}));

### نمونه بهینه
const computedRows = computed(() => 
productRows.map(row => ({
...row,
total: row.price * row.qty,
}))
);

با این روش، محاسبات بهینه‌تر انجام می‌شود و از ایجاد چندین مقدار computed غیرضروری جلوگیری خواهد شد.

#️⃣#tip #vue
👥@IR_javascript_group
🆔@IR_javascript
👍1
ساده‌ترین روش برای نمایش «دیروز» در هر زبانی، استفاده از Relative TimeFormat است!

یکی از ابزارهای موردعلاقه من برای کار با تاریخ، Intl.RelativeTimeFormat است. این ابزار به شما امکان می‌دهد زمان نسبی را به‌صورت زیبا نمایش دهید، مانند: «۵ دقیقه پیش»، «دو روز بعد»، «فردا» — و همه این موارد به‌طور خودکار با توجه به تنظیمات زبانی کاربر انجام می‌شود!

### مثال پایه
فرض کنید می‌خواهیم زمان انتشار یک پست را نمایش دهیم:
const rtf = new Intl.RelativeTimeFormat('fa', { numeric: 'auto' });

console.log(rtf.format(-1, 'day')); // "دیروز"
console.log(rtf.format(2, 'week')); // "دو هفته بعد"

### پارامترهای کلیدی:
‏- 'fa' – زبان موردنظر که می‌توانید آن را به 'en'`، 'fr'، 'ja'` و ... تغییر دهید.
‏- `numeric: 'auto' – باعث می‌شود به‌جای «۱ روز پیش»، عبارت «دیروز» نمایش داده شود. مقدار پیش‌فرض این گزینه 'always' است، که در این صورت `format(-1, 'day') مقدار «۱ روز پیش» را برمی‌گرداند، نه «دیروز».

### پارامترهای متد `format`
✔️ آرگومان اول – عددی که مشخص می‌کند زمان چقدر از لحظه‌ی فعلی فاصله دارد.
✔️ آرگومان دوم – واحد زمانی مانند second`، `minute`، `hour`، `day`، `week`، `month`، `year.

### چند نمونه دیگر:
console.log(rtf.format(-3, 'month')); // "سه ماه پیش"
console.log(rtf.format(1, 'year')); // "سال آینده"

حالا دیگر نیازی به نوشتن توابع دستی برای نمایش «۵ دقیقه پیش» ندارید! می‌توانید به‌سادگی از Intl.RelativeTimeFormat استفاده کنید.

نظر شما درباره‌ی این ابزار چیست؟ تا به حال در پروژه‌هایتان از آن استفاده کرده‌اید؟

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
3🔥3
در هسته‌ی Nuxt قابلیت هیدراسیون تنبل (Lazy Hydration) اضافه شده است—پس دیگر نیازی به ساخت راهکارهای سفارشی نیست! اما بیایید قدم‌به‌قدم بررسی کنیم.

### 🚀 Vue 3.5 و ارتقای SSR
در Vue 3.5 SSR بهبود یافته و حالا کامپوننت‌های غیرهم‌زمان (Async Components) می‌توانند زمان هیدراته شدن خود را از طریق استراتژی hydrate در API `defineAsyncComponent()` کنترل کنند. مثلاً، می‌توان تعیین کرد که یک کامپوننت فقط هنگام نمایش در صفحه هیدراته شود:
import { defineAsyncComponent, hydrateOnVisible } from 'vue';

const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnVisible()
});

### 🔥 Nuxt 3.16 و پشتیبانی از Delayed Hydration
📅 در ۷ مارس ۲۰۲۵ نسخه‌ی Nuxt 3.16 منتشر شد که شامل ویژگی‌های جدید، بهبود عملکرد، ابزارهای توسعه‌ی بهتر و... بود، اما مهم‌ترین قابلیت اضافه‌شده، Delayed Hydration است. توسعه‌دهندگان Nuxt در وبلاگ خود جزئیات این قابلیت را توضیح داده‌اند.

#### مثال: هیدراته شدن کامپوننت هنگام ورود به ویوپورت
<!-- کامپوننت تنها زمانی هیدراته می‌شود که در محدوده‌ی دید کاربر قرار بگیرد -->
<LazyExpensiveComponent hydrate-on-visible />

حالا کامپوننت فقط وقتی در زون دید کاربر قرار بگیرد، لود و اجرا می‌شود—دیگر نیازی به نوشتن اسکریپت‌های پیچیده برای ردیابی و بارگذاری نیست.

🔹 همچنین می‌توان از hook جدید `hydrated` برای اجرای دستورات خاص پس از هیدراته شدن کامپوننت استفاده کرد:
<LazyComponent hydrate-on-visible @hydrated="onComponentHydrated" />

### 📌 چه خبر؟
🔹 این ویژگی تنظیمات مختلفی دارد که می‌توانید نحوه‌ی کارکرد آن را در مستندات Nuxt مطالعه کنید.
🔹 Nuxt به‌سرعت در حال پیشرفت است و وقت آن رسیده که این قابلیت را در محیط Production تست کنیم.
🔹 شاید وقتش رسیده که کامپوننت‌های سمت سرور (Server Components) را هم امتحان کنیم؟ (قابلیت آزمایشی nuxt-islands – اما این بحث را می‌گذاریم برای بعد 😉)

#️⃣#tip #vue
👥@IR_javascript_group
🆔@IR_javascript
👍1
معمولاً برای گروه‌بندی یک آرایه بر اساس یک کلید خاص، از متد reduce استفاده می‌شود.
اما اکنون متدهای جدیدی مانند Object.groupBy و Map.groupBy در دسترس هستند که این فرآیند را بسیار ساده‌تر می‌کنند.

مثال در جاوااسکریپت:

const users = [
{ id: ۱، name: "Alex"، role: "admin" },
{ id: ۲، name: "Anna"، role: "user" },
];

const grouped = Object.groupBy(users, ({ role }) => role);

// نتیجه:
// {
// admin: [{...}],
// user: [{...}]
// }


این روش، کدی خواناتر و ساخت‌یافته‌تر ارائه می‌دهد و مناسبِ زمانی‌ست که بخواهید داده‌ها را بر اساس یک ویژگی مشخص دسته‌بندی کنید، بدون نیاز به نوشتن منطق دستی در reduce.


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
🔥5
This media is not supported in your browser
VIEW IN TELEGRAM
هسته‌ی Jupyter برای javascript

‏Deno به‌صورت پیش‌فرض همراه با یک هسته‌ی Jupyter ارائه می‌شود که امکان نوشتن JavaScript و TypeScript را در محیط‌های تعاملی نوت‌بوک فراهم می‌سازد. در این محیط می‌توانید به‌سادگی از APIهای وب و Deno استفاده کرده و حتی بسته‌های npm را مستقیماً در نوت‌بوک خود وارد (import) کنید.

🔗https://docs.deno.com/runtime/reference/cli/jupyter/
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
🔥1
### چرا باید از استفاده کنترل‌نشده از watch پرهیز کرد؟

#### یک. برهم خوردن جریان واکنشی (Reactive Flow)
استفاده از watch گاهی باعث پنهان شدن منطق واکنش‌گرایی می‌شود. برای مثال:

watch(() => userId.value, async (newId) => {
const data = await fetchUserData(newId);
userData.value = data;
});


در ظاهر ساده است، اما وقتی همین کد در چند مؤلفه با داده‌های مشابه یا اثرات جانبی دیگر تکرار شود، کنترل جریان داده‌ها سخت‌تر شده و رفتار برنامه غیرقابل پیش‌بینی می‌شود.

Vue معماری واکنش‌گرای یک‌طرفه (از داده به UI) را ترویج می‌دهد، اما watch اغلب برای تبدیل داده به داده استفاده می‌شود که جریان را پیچیده می‌کند و اشکال‌زدایی را دشوارتر می‌سازد.

#### دو. کاهش خوانایی و دشواری در نگه‌داری

وقتی در یک مؤلفه چندین watch به‌صورت زنجیره‌ای نوشته شوند، ساختار کد به‌شکل کلاف سردرگمی درمی‌آید که فهم آن زمان‌بر است:

watch(() => settings.value.theme, applyTheme);
watch(() => settings.value.language, loadTranslations);
watch(() => settings.value.notifications, updateNotifications);


این نوع پراکندگی منطق در `watch`ها:

- خوانایی کد را پایین می‌آورد
- بازسازی (refactor) و انتقال منطق به composableها را دشوار می‌کند

در مقابل، می‌توان این رفتارها را در یک computed مجتمع یا composable مشخص و مستقل تعریف کرد.

#### سه. تأثیر منفی بر عملکرد

مثلاً اگر یک شیء عمیق را با deep: true مانیتور کنید:

watch(settings, (newVal, oldVal) => {
// مقایسه عمیق کل ساختار
}, { deep: true });


اگر settings شامل اشیاء یا آرایه‌های بزرگ باشد، عملکرد برنامه افت خواهد کرد. از طرفی، بسیاری از کاربردهای watch را می‌توان با computed حل کرد:

const isDarkTheme = computed(() => settings.value.theme === 'dark');


ویژگی‌های محاسبه‌شونده کش می‌شوند، سبک‌تر هستند و اجرای بهتری دارند.

#### چهار. خطر نشت حافظه (Memory Leak)

در مؤلفه‌هایی که با شرط v-if نمایش داده می‌شوند، اگر watch بدون مدیریت درست ایجاد شود:

<template>
<div v-if="showComponent">
<SomeComponent />
</div>
</template>


و در SomeComponent بنویسیم:

watch(async() => someReactiveValue.value, await doSomething);


اگر این watch هنگام نابودی مؤلفه (unmount) لغو نشود، به‌مرور زمان منابع حافظه را اشغال کرده و باعث نشت حافظه می‌شود.

---

### راهکار پیشنهادی

قبل از استفاده از `watch`، ابتدا این پرسش را از خودتان بپرسید:

> «آیا می‌توان این مسئله را با `computed`، `props` یا emit حل کرد؟»

در بسیاری از موارد پاسخ *بله* است، و انتخاب راه‌حل‌های اعلانی (declarative) باعث خوانایی، عملکرد بهتر و مدیریت ساده‌تر خواهد شد.



#️⃣#tip #vue
👥@IR_javascript_group
🆔@IR_javascript
👍1
سایت pollinations.ai یکی از رابط‌های برنامه‌نویسی (API) مناسب و ساده برای تولید تصاویر با استفاده از هوش مصنوعی است.

### مثال درخواست GET برای دریافت متن:
GET https://text.pollinations.ai/{prompt}


### نمونه استفاده در کد HTML:
<img 
src="https://image.pollinations.ai/prompt/A%20modern%20and%20vibrant%20digital%20illustration%20of%20a%20Telegram%20channel%20called%20%22IR_javascript%22,%20focused%20on%20web%20development,%20JavaScript,%20and%20coding.%20The%20scene%20features%20glowing%20code%20snippets,%20stylized%20browser%20windows,%20Iranian%20cultural%20motifs%20subtly%20integrated%20into%20the%20design,%20and%20futuristic%20UI%20elements.%20The%20background%20includes%20icons%20of%20JavaScript%20(JS),%20HTML,%20and%20CSS,%20with%20a%20dark%20mode%20theme%20and%20a%20sleek%20techy%20look.%20Emphasize%20community,%20learning,%20and%20innovation.%20Perfect%20for%20a%20tech%20advertisement%20poster."
alt="تصویری زیبا"
/>


در تصویر بالا، نمونه‌ای از تولید تصویر توسط مدل DALL-E نسخهٔ سوم نمایش داده شده که از متن داخل بلاک کد (prompt) استفاده کرده است.


#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
🔥2
همه‌چیز درباره‌ی favicon
🔻 چرا این مقاله مهمه؟
چون ساخت آیکون برای سایت به یه کابوس تبدیل شده؛ بیش از ۲۰ فایل PNG با اندازه‌های مختلف فقط برای نمایش یه لوگوی ساده؟ واقعاً لازمه؟ نه!
### نسخه‌ی بسیار خلاصه
به‌جای ساخت ده‌ها آیکون، کافی‌ست تنها پنج آیکون و یک فایل JSON ایجاد کنیم.

کد HTML:

<link rel="icon" href="/favicon.ico" sizes="any"><!-- سی‌ودو در سی‌ودو پیکسل -->
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png"><!-- صد و هشتاد در صد و هشتاد پیکسل -->
<link rel="manifest" href="/manifest.webmanifest">


و فایل مانیفست وب:

// manifest.webmanifest
{
"icons": [
{ "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
{ "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" }
]
}


همین و بس. اگر مایلید بدانید چطور به این نتیجه رسیدم، ادامه‌ی مقاله را بخوانید.


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
### مجموعه ایده‌آل فاوآیکون‌ها

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

- مرورگرها فاوآیکون‌ها را در پس‌زمینه بارگذاری می‌کنند، بنابراین اندازه بزرگ فایل فاوآیکون تأثیری بر عملکرد وب‌سایت ندارد

‏- SVG روشی عالی برای کاهش اندازه آیکون‌ها برای تصاویری است که نباید به‌صورت شطرنجی باشند؛ برای بسیاری از لوگوها، فایل نهایی بسیار کوچک‌تر از PNG خواهد بود.

با داشتن تنها سه فایل PNG، می‌توان آن‌ها را با دقت بیشتری بهینه‌سازی کرد و با استفاده از تنظیمات مناسب در ابزارهای پیشرفته، کیفیت را حفظ نمود. این کار مشکل کاربرانی را که برای هر مگابایت اینترنت هزینه می‌پردازند، حل می‌کند.

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

‏### favicon.ico برای مرورگرهای قدیمی

فایل‌های ICO می‌توانند چندین نسخه از تصویر با اندازه‌های مختلف را در خود جای دهند. توصیه می‌کنم از یک تصویر با اندازه ۳۲×۳۲ پیکسل استفاده کنید. نسخه ۱۶×۱۶ را تنها در صورتی ایجاد کنید که لوگو در این اندازه وضوح خود را از دست ندهد یا طراح شما بتواند آن را به‌صورت دستی برای این اندازه بازطراحی کند.

بهتر است این فایل را در مسیر دقیق https://example.com/favicon.ico نگهداری کنید، بدون استفاده از مسیرهای پیچیده. برخی ابزارها، مانند خوانندگان RSS، مستقیماً /favicon.ico را از سرور درخواست می‌کنند و به دنبال آن در مکان دیگری نمی‌گردند.

برای رفع اشکال Chrome که به‌جای SVG، فایل ICO را انتخاب می‌کند، به sizes="any" برای تگ <link> مربوط به فایل .ico نیاز داریم.

### یک آیکون SVG با نسخه‌های روشن/تاریک برای مرورگرهای مدرن

SVG یک فرمت برداری است که به‌جای پیکسل‌ها، منحنی‌ها را توصیف می‌کند. در اندازه‌های بزرگ، این روش کارآمدتر از تصاویر شطرنجی است. در زمان نگارش این مقاله، ۷۲٪ از مرورگرها از آیکون‌های SVG پشتیبانی می‌کنند.

صفحه HTML شما باید دارای تگ <link> در بخش <head> با rel="icon"`، `type="image/svg+xml" و href باشد که به فایل SVG با این ویژگی‌ها اشاره می‌کند

‏SVG یک فرمت XML است و می‌تواند تگ <style> برای توصیف CSS داشته باشد. مانند هر CSS دیگری، می‌تواند شامل مدیا کوئری‌هایی مانند @media (prefers-color-scheme: dark) باشد. این امکان را می‌دهد که همان آیکون بین تم‌های روشن و تاریک سیستم تغییر کند.

### تصویر PNG با اندازه ۱۸۰×۱۸۰ برای دستگاه‌های اپل

آیکون Apple touch تصویری برای صفحه اصلی iPhone یا iPad است. در صفحه HTML شما باید تگی با <link rel="apple-touch-icon" href="apple-touch-icon.png"> در بخش <head> وجود داشته باشد.

از iOS 8 به بعد، برای iPad به تصویری با رزولوشن ۱۸۰×۱۸۰ نیاز است. دستگاه‌های قدیمی‌تر آن را به‌صورت خودکار مقیاس‌دهی می‌کنند.

نکته‌ای کوچک: آیکون Apple touch بهتر به نظر می‌رسد اگر حاشیه‌ای به اندازه ۲۰ پیکسل داشته باشد و رنگ پس‌زمینه‌ای اضافه شود. برای این کار می‌توانید از هر ویرایشگر گرافیکی استفاده کنید.

### مانیفست وب‌اپلیکیشن با آیکون‌های PNG ۱۹۲×۱۹۲ و ۵۱۲×۵۱۲ برای دستگاه‌های اندروید

- مانیفست وب‌اپلیکیشن یک فایل JSON است که تمامی جزئیات لازم برای نصب وب‌سایت شما به‌عنوان یک اپلیکیشن سیستمی را فراهم می‌کند. این فرمت توسط گوگل در چارچوب ابتکار PWA معرفی شده است.

- صفحه HTML شما باید تگی با <link rel="manifest" href="manifest.webmanifest"> داشته باشد که به فایل مانیفست اشاره می‌کند.
- در مانیفست باید فیلد icons وجود داشته باشد که به دو آیکون اشاره می‌کند: ۱۹۲×۱۹۲ برای نمایش در صفحه اصلی و ۵۱۲×۵۱۲ که به‌عنوان صفحه بارگذاری PWA استفاده می‌شود


{
"icons": [
{ "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
{ "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" }
]
}



#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
«واترمارک» در سال ۲۰۲۵

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

امروز به شما نشان خواهم داد که چگونه به راحتی و به سرعت می‌توان یک واترمارک برای سایت خود با استفاده از SVG و جاوااسکریپت ایجاد کرد.

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

اگرچه HTML و CSS استاندارد باقی می‌مانند، این ویژگی ایجاد واترمارک است که توجه ویژه‌ای را می‌طلبد.

کد تابع ایجاد واترمارک SVG:

 function generateWatermarkSVG(text) {
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="100">
<text x="15" y="70"
transform="rotate(-15, 10, 10)"
fill="rgba(60, 90, 150, 0.16)"
font-size="18"
font-family="sans-serif">
${text}
</text>
</svg>
`.trim();

const encodedSvg = new TextEncoder().encode(svg);
return `data:image/svg+xml;base64,${btoa(String.fromCharCode(...encodedSvg))}`;
}

window.addEventListener("DOMContentLoaded", function() {
const watermarkText = "@IR_javascript";
const container = document.getElementById('watermark-container');

const encodedSvg = generateWatermarkSVG(watermarkText);
container.style.setProperty('background-image', `url(${encodedSvg})`);
});


چگونه کد کار می‌کند؟
ساختار SVG:
داخل رشته SVG یک عنصر SVG ایجاد می‌شود که متنی را نمایش می‌دهد، این متن با ویژگی‌های HTML از جمله موقعیت، چرخش و شفافیت تنظیم می‌شود؛
• کدگذاری در Base64:
برای استفاده از این SVG به عنوان پس‌زمینه، از کدگذاری Base64 استفاده می‌شود. این امکان را فراهم می‌کند که تصویر مستقیماً در CSS گنجانده شود، بدون نیاز به بارگذاری فایل جداگانه؛
• روش btoa:
تابع btoa() رشته را به فرمت Base64 تبدیل می‌کند. با این حال، برای کار درست با نویسه‌های سیریلیک و دیگر نمادهای غیر-ASCII، باید از روشی با استفاده از TextEncoder بهره گرفت. این روش به جلوگیری از مشکلات کدگذاری کمک خواهد کرد.



#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
رابط برنامه‌نویسی View Transitions
نوآوری تازه‌ای است که امکان ایجاد انیمیشن‌های نرم و طبیعی بین حالت‌های مختلف یک صفحه را به سادگی فراهم می‌کند.

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

### نمونه‌ای ساده:

document.startViewTransition(() => 
updateTheDOMSomehow());


در اینجا متد
document.startViewTransition()
فراخوانی می‌شود و یک تابع که شامل تغییرات در DOM است به آن ارسال می‌گردد. ادامه‌ی فرآیند توسط مرورگر انجام می‌شود.

### این فرآیند چگونه عمل می‌کند؟
مرورگر ابتدا وضعیت فعلی صفحه را «ذخیره» می‌کند، سپس تغییرات را اعمال کرده و وضعیت قبلی و جدید را با یکدیگر مقایسه می‌کند. در نهایت، خود مرورگر عملیات انیمیشن بین این دو حالت را مدیریت می‌نماید.

آنچه این ویژگی را بسیار جذاب‌تر می‌سازد، امکان اتصال مستقیم انیمیشن‌های CSS از طریق شبه‌المان‌هایی مانند
::view-transition-old و ::view-transition-new
است. این قابلیت، انعطاف‌پذیری بیشتری را فراهم می‌کند: برای نمونه می‌توان برای حالت قبلی افکت محو شدن و برای حالت جدید افکت بزرگ‌نمایی در نظر گرفت.


🔗https://codepen.io/katrin_profrontend/pen/OPJqBOx
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
کلون‌کردن عمیق اشیاء واکنش‌پذیر در Vue 3

Vue 3 از *Proxy* برای واکنش‌پذیری استفاده می‌کند، که این مسئله زمانی که بخواهید اشیاء را کلون کنید، مشکلاتی ایجاد می‌کند. روش‌های استاندارد به صورت مورد انتظار عمل نمی‌کنند:

const state = reactive({ user: { name: "Al" } });
// مشکلات:
const badCopy1 = { ...state }; // حفظ ارجاع به Proxy
const badCopy2 = JSON.parse(JSON.stringify(state)); // از دست دادن متدها و Proxy


استفاده از عملگر `...` یا مشابه آن:
اگر شما از عملگر ... برای کپی کردن یک شیء واکنش‌پذیر استفاده کنید، این کپی فقط ارجاع به *Proxy* اصلی را می‌گیرد، نه خود داده‌ها. به عبارت دیگر، تغییرات در شیء جدید می‌تواند بر شیء اصلی تأثیر بگذارد.


استفاده از `JSON.stringify` و `JSON.parse`:
وقتی از JSON.stringify برای تبدیل شیء به یک رشته و سپس از JSON.parse برای بازگشایی آن استفاده می‌کنید، متدهای شیء واکنش‌پذیر از بین می‌روند و همچنین ارتباط با *Proxy* قطع می‌شود. این به این معناست که ویژگی‌های واکنش‌پذیری از دست می‌روند و این کپی دیگر به صورت واکنش‌پذیر نخواهد بود.


### چرا این اتفاق می‌افتد؟
Vue 3 از *Proxy* برای ایجاد واکنش‌پذیری استفاده می‌کند. *Proxy* در واقع یک "نظارت‌کننده" بر روی اشیاء است که هر تغییر در داده‌ها را شبیه‌سازی می‌کند و به Vue اطلاع می‌دهد تا رابط کاربری به‌روز شود. زمانی که شما از روش‌های استاندارد کپی استفاده می‌کنید، این ویژگی‌های *Proxy* نادیده گرفته می‌شوند و به همین دلیل نمی‌توانید از کپی‌های انجام‌شده به‌طور کامل استفاده کنید.

سه روش کاربردی برای این کار:

1. ترکیب toRaw و structuredClone

import { toRaw } from 'vue';
const original = reactive({ data: 123 });
const copy = structuredClone(toRaw(original));


2. کپی‌کردن عمیق دستی

function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const clone = Array.isArray(obj) ? [] : {};
for (const key in obj) {
clone[key] = deepClone(obj[key]);
}
return clone;
}
const copy = reactive(deepClone(toRaw(original)));


3. استفاده از کتابخانه‌های موجود

import { cloneDeep } from 'lodash-es';
const copy = reactive(cloneDeep(toRaw(obj)));


این روش‌ها به شما کمک می‌کنند تا اشیاء واکنش‌پذیر را به طور صحیح کلون کنید بدون اینکه ارجاع به *Proxy* حفظ شود یا متدهای آن از بین بروند.

#️⃣#tip #vue
👥@IR_javascript_group
🆔@IR_javascript
👍2
در این پست، API جدیدی با نام effectScope() برای پکیج @vue/reactivity معرفی می‌شود. یک نمونه از EffectScope می‌تواند به‌صورت خودکار، افکت‌هایی را که درون یک تابع هم‌زمان اجرا می‌شوند، گردآوری کند تا در زمان مناسب همگی با هم حذف شوند.

در تابع setup() درون یک کامپوننت Vue، افکت‌ها به‌صورت خودکار جمع‌آوری شده و به همان نمونه‌ی کامپوننت متصل می‌شوند. هنگام از بین رفتن کامپوننت، این افکت‌ها نیز به‌طور خودکار حذف می‌شوند. این ویژگی، ساده و شهودی است.

اما هنگامی که این افکت‌ها را خارج از کامپوننت‌ها یا در قالب یک پکیج مستقل استفاده کنیم، کار به این سادگی نیست. برای نمونه، حذف افکت‌های computed و watch به صورت دستی به شکل زیر خواهد بود:
const disposables = []

const counter = ref(۰)
const doubled = computed(() => counter.value * ۲)

disposables.push(() => stop(doubled.effect))

const stopWatch۱ = watchEffect(() => {
console.log(`counter: ${counter.value}`)
})

disposables.push(stopWatch۱)

const stopWatch۲ = watch(doubled, () => {
console.log(doubled.value)
})

disposables.push(stopWatch۲)


و برای حذف آن‌ها:
disposables.forEach((f) => f())
disposables = []


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

اما با وجود EffectScope بصورت زیر میشود
// effect، computed، watch، و watchEffect که درون این scope ساخته می‌شوند، جمع‌آوری خواهند شد

const scope = effectScope()

scope.run(() => {
const doubled = computed(() => counter.value * ۲)

watch(doubled, () => console.log(doubled.value))

watchEffect(() => console.log('تعداد: ', doubled.value))
})

// برای حذف همه افکت‌ها در این scope
scope.stop()





‏### Scopeهای تودرتو

‏Scopeهای تودرتو نیز باید توسط scope والد جمع‌آوری شوند. زمانی که scope والد حذف شود، همه‌ی scopeهای فرزند نیز حذف خواهند شد:

const scope = effectScope()

scope.run(() => {
const doubled = computed(() => counter.value * ۲)

effectScope().run(() => {
watch(doubled, () => console.log(doubled.value))
})

watchEffect(() => console.log('تعداد: ', doubled.value))
})

// حذف تمام افکت‌ها، از جمله موارد در scopeهای تودرتو
scope.stop()


---

‏### Scopeهای جداشده (Detached)

پارامتر detached در effectScope اجازه می‌دهد تا scope به‌صورت جدا از scope والد ایجاد شود. این ویژگی برای سناریوهایی مانند «مقداردهی اولیه تنبل» (Lazy Initialization) مفید است.

let nestedScope

const parentScope = effectScope()

parentScope.run(() => {
const doubled = computed(() => counter.value * ۲)

nestedScope = effectScope(true /* detached */)
nestedScope.run(() => {
watch(doubled, () => console.log(doubled.value))
})

watchEffect(() => console.log('تعداد: ', doubled.value))
})

// فقط scope والد حذف می‌شود
parentScope.stop()

// scope تودرتو را در زمان مناسب حذف می‌کنیم
nestedScope.stop()


---

### تابع onScopeDispose

تابع onScopeDispose عملکردی مشابه onUnmounted دارد، اما برای scope فعلی (نه نمونه کامپوننت). این قابلیت به فانکشن‌های ترکیبی کمک می‌کند تا اثرات جانبی خود را همراه با scope مربوطه پاک‌سازی کنند.

import { onScopeDispose } from 'vue'

const scope = effectScope()

scope.run(() => {
onScopeDispose(() => {
console.log('پاک‌سازی شد!')
})
})

scope.stop() // چاپ: پاک‌سازی شد!


---

### دریافت scope فعلی

‏API جدید getCurrentScope() برای دریافت scope فعلی معرفی شده است:

import { getCurrentScope } from 'vue'

getCurrentScope() // EffectScope | undefined


🔗https://github.com/vuejs/rfcs/blob/master/active-rfcs/0041-reactivity-effect-scope.md
#️⃣#tip #vue
👥@IR_javascript_group
🆔@IR_javascript
### مثال‌ها

#### مثال الف: استفاده‌ی مشترک از composable

تابع useMouse() یک نمونه‌ی خوب برای ایجاد اثرات جانبی جهانی است:

function useMouse() {
const x = ref(۰)
const y = ref(۰)

function handler(e) {
x.value = e.x
y.value = e.y
}

window.addEventListener('mousemove', handler)

onUnmounted(() => {
window.removeEventListener('mousemove', handler)
})

return { x, y }
}


اگر useMouse() در چند کامپوننت فراخوانی شود، هرکدام یک listener جدید ایجاد کرده و refs جداگانه خواهند داشت. برای جلوگیری از این کار پرهزینه، می‌توانیم از scope جداشده و onScopeDispose استفاده کنیم:

ابتدا:

- onUnmounted(() => {
+ onScopeDispose(() => {
window.removeEventListener('mousemove', handler)
})


سپس یک تابع کمکی برای مدیریت اشتراک ایجاد می‌کنیم:

function createSharedComposable(composable) {
let subscribers = ۰
let state, scope

const dispose = () => {
if (scope && --subscribers <= ۰) {
scope.stop()
state = scope = null
}
}

return (...args) => {
subscribers++
if (!state) {
scope = effectScope(true)
state = scope.run(() => composable(...args))
}
onScopeDispose(dispose)
return state
}
}


اکنون:

const useSharedMouse = createSharedComposable(useMouse)


این نسخه از useMouse تنها یک بار listener را اضافه می‌کند و در صورت عدم نیاز، آن را حذف می‌نماید.

---

#### مثال ب: Scopeهای موقتی (Ephemeral)

export default {
setup() {
const enabled = ref(false)
let mouseState, mouseScope

const dispose = () => {
mouseScope && mouseScope.stop()
mouseState = null
}

watch(
enabled,
() => {
if (enabled.value) {
mouseScope = effectScope()
mouseState = mouseScope.run(() => useMouse())
} else {
dispose()
}
},
{ immediate: true }
)

onScopeDispose(dispose)
},
}


در اینجا scopeها به‌صورت موقتی ساخته و حذف می‌شوند. onScopeDispose اطمینان حاصل می‌کند که useMouse به‌درستی پاک‌سازی شود، درحالی‌که onUnmounted در این فرآیند فراخوانی نمی‌شود.

---

### تأثیر در هسته Vue

در حال حاضر، در @vue/runtime-dom توابع computed بازنویسی می‌شوند تا به نمونه‌ی کامپوننت متصل شوند. بنابراین موارد زیر معادل نیستند:

// متفاوت
import { computed } from '@vue/reactivity'
import { computed } from 'vue'


با پیاده‌سازی این RFC، دیگر نیازی به بازنویسی computed نخواهد بود و vue می‌تواند مستقیماً از نسخه‌ی @vue/reactivity استفاده کند.


🔗https://github.com/vuejs/rfcs/blob/master/active-rfcs/0041-reactivity-effect-scope.md
#️⃣#tip #vue
👥@IR_javascript_group
🆔@IR_javascript
درک تفاوت‌ها: File در برابر Blob
گاهی اوقات در کد به موقعیت‌هایی برمی‌خوریم که به‌دلیل درک نادرست از تفاوت میان Blob و File**، ابهاماتی به‌وجود می‌آید. سپس این پرسش‌ها مطرح می‌شوند:
«چرا نام فایل منتقل نمی‌شود؟»
«چرا تاریخ وجود ندارد؟»
یا «چطور می‌توان این را به‌درستی با FormData ارسال کرد؟»
زمان آن رسیده که موضوع را روشن کنیم!

### Blob چیست؟
**Blob
ساختاری داده‌ای است که یک شیء تغییرناپذیر شامل داده‌های دودویی را نمایش می‌دهد.

const blob = new Blob(["Hello, world!"], { type: "text/plain" });


نکات مهم:
- می‌تواند هر نوع داده‌ای را در خود جای دهد: تصویر، متن، ویدیو و ...
- اطلاعاتی درباره‌ی منبع فایل در خود ندارد؛
- فاقد نام (name) و تاریخ آخرین تغییر (lastModified) است؛
- اما می‌توان به اندازه (size) و نوع (type) آن دسترسی داشت.

کاربردهای رایج Blob:
- تولید فایل‌ها به‌صورت «لحظه‌ای» (مثلاً تولید فایل CSV یا JSON از رابط کاربری)
- ایجاد URL با استفاده از URL.createObjectURL()
- نگهداری موقت داده‌ها در حافظه

جمع‌بندی:
Blob روشی عمومی و انعطاف‌پذیر برای نمایش هرگونه داده‌ی «خام» در یک وب‌اپلیکیشن است.
در بسیاری از عملیات مربوط به محتوای دودویی در وب، پایه و اساس را Blob تشکیل می‌دهد.

---

### File چیست؟
File در واقع نسخه‌ی توسعه‌یافته‌ی Blob به همراه فراداده (metadata) است.

const file = new File(["Hello, world!"], "greeting.txt", {
type: "text/plain",
lastModified: Date.now()
});


نکات مهم:
- از Blob ارث‌بری می‌کند؛ یعنی تمام قابلیت‌های Blob را داراست؛
- افزوده‌هایی دارد مانند: name و lastModified;
- شیء File همان چیزی است که هنگام انتخاب فایل توسط کاربر از طریق <input type="file"> به‌دست می‌آید.

کاربردهای رایج File:
- مدیریت فایل‌هایی که کاربر در اختیار برنامه قرار می‌دهد؛
- خواندن محتوای فایل با FileReader
- ارسال فایل به سرور
- پیش‌نمایش فایل در مرورگر

---

### نتیجه‌گیری نهایی:
🔹 اگر فقط به یک جریان داده‌ی دودویی نیاز دارید: Blob را انتخاب کنید.
🔹 اگر نیاز به شیئی دارید که همانند یک فایل رفتار کند: File انتخاب بهتری است.

و همین!

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍2
مهاجرت مایکروسافت به موتور کرومیوم یک تصمیم بسیار هوشمندانه بود.

در آغوش بگیر، توسعه بده، و حذف کن — راهبردی که هنوز هم کار می‌کند.
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
عبارت «در آغوش بگیر، گسترش بده و نابود کن» (Embrace, Extend, and Extinguish – EEE) عبارتی است که، بنا بر گزارش وزارت دادگستری ایالات متحده، مایکروسافت در داخل شرکت برای توصیف راهبرد خود به کار می‌برد. این استراتژی به این صورت عمل می‌کرد که ابتدا وارد حوزه‌هایی با استانداردهای رایج و پرکاربرد می‌شد، سپس آن استانداردها را با قابلیت‌های اختصاصی خود گسترش می‌داد، و در نهایت از این تفاوت‌ها برای به حاشیه راندن و تضعیف جدی رقبا استفاده می‌کرد.


فرآیند «در آغوش‌گیری و گسترش» مایکروسافت در حوزه توسعه نرم‌افزار به‌وضوح قابل مشاهده است:
از Visual Studio Code و تملک GitHub و npmjs.org گرفته تا معرفی TypeScript، و اکنون Copilot (محصولی بر پایه هوش مصنوعی OpenAI).

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍2
روندهای تاریخی در آمار استفاده از کتابخانه‌های جاوااسکریپت در وب‌سایت‌ها
این گزارش، روندهای تاریخی مربوط به میزان استفاده از برترین کتابخانه‌های جاوااسکریپت را از آوریل سال دوهزار و بیست‌وچهار تا به امروز بررسی می‌کند.

🔗https://w3techs.com/technologies/history_overview/javascript_library/all
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
دسترس‌پذیری وب‌سایت و Screen Reader
این موضوعی‌ست که انگار همه با آن آشنایی دارند، اما کمتر کسی آن را واقعاً درک می‌کند.

در توسعه‌ی فرانت‌اند، معمولاً تمرکز ما بر ظاهر، عملکرد و تعامل‌پذیری است. اما دسترس‌پذیری چطور؟ معمولاً به آن می‌گوییم: «باشه، بعداً اضافه‌اش می‌کنیم.» تصور می‌شود که این فقط مربوط به موقعیت‌های خاص و کاربران «خاص» است. اما واقعیت این است که دسترس‌پذیری برای آدم‌های عادی‌ست**—تنها با ابزارها و شرایط متفاوتی برای تعامل با رابط کاربری.

Screen reader ظاهر را نمی‌بیند؛ **ساختار را می‌خواند.
او «نمی‌بیند» که یک دکمه زیباست، بلکه تنها می‌فهمد این عنصر چیست (دکمه، بلوک، عنوان و غیره)، کجای ساختار DOM قرار گرفته (که در اولویت خواندن اثر دارد)، و آیا می‌توان با آن تعامل داشت یا نه (مثل دکمه‌ها، فیلدهای ورودی و موارد دیگر).

براساس همین اطلاعات، Screen reader تصمیم می‌گیرد که آیا لازم است این عنصر را برای کاربر بخواند یا نه.

---

### از پایه شروع کنیم:
چه چیزهایی لازم است تا چنین شیوه‌ی تعامل با رابط، در سایت قابل استفاده باشد؟

#### استفاده از تگ‌های معنایی درست
تگ‌هایی مانند: button`، `nav`، `main`، `label`، `form
این‌ها صرفاً «استانداردهای رایج» نیستند؛ بلکه زبانی هستند که اسکرین‌ریدرها و APIهای دسترس‌پذیری از طریق آن با رابط کاربر ارتباط برقرار می‌کنند.

#### 🖼 متن جایگزین برای تصاویر
اگر تصویر حاوی معناست، همیشه ویژگی alt را با توضیح مناسب قرار بده.
اگر تصویر صرفاً تزئینی است، alt="" را تنظیم کن تا screen reader آن را نادیده بگیرد.

#### ⌨️ فوکوس درست و ناوبری با کیبورد
با کلید Tab در وب‌سایت حرکت کن.
آیا ترتیب منطقی دارد؟
آیا تمام عناصر قابل فوکوس هستند؟
آیا «تله»‌ای وجود دارد که کاربر در آن گیر کند؟
اگر کاربر نتواند به یک دکمه برسد یا در ترتیب بین عناصر گم شود، این خود یک مانع است.

#### 🧠 استفاده هوشمندانه از ویژگی‌های aria-*
هر جا امکان استفاده از تگ های معنایی بومی HTML وجود دارد، از آن استفاده کن.
ویژگی‌هایی مانند aria-label`، `aria-hidden`، `aria-live و ... زمانی کاربرد دارند که HTML نتواند نیاز مورد نظر را برآورده کند.
اما اگر بدون دلیل موجه، از آن‌ها استفاده کنی، فقط کد را سنگین‌تر و تجربه‌ی کاربر را مختل خواهی کرد.

---

### نکته‌ی کلیدی:
دسترس‌پذیری «مخصوص دیگران» نیست.
این نه یک پیچیدگی‌ست، نه باری اضافی؛ بلکه بخشی از همان توجهی‌ست که به طراحی واکنش‌گرا، مسیر ناوبری واضح یا سرعت بارگذاری می‌دهی.
دسترس‌پذیری، فقط یک روش متفاوت و جایگزین برای تعامل با رابط کاربری توست.

و جالب اینجاست که دسترس‌پذیری معمولاً از ساده‌ترین جا آغاز می‌شود:
نه با افزونه‌های پیشرفته یا ترفندهای پیچیده، بلکه با کدنویسی تمیز، شفاف، و دغدغه‌مندی برای کاربران واقعی.



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