برنامه نویسی 257 بازدید

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

  • اپلیکیشن‌های تک‌صفحه‌ای
  • رویکرد اعلانی ری‌اکت
  • تغییرناپذیری
  • محض بودن یا purity
  • ترکیب‌بندی
  • DOM مجازی
  • گردش داده غیر جهت‌دار

اپلیکیشن‌های تک‌صفحه‌ای

اپلیکیشن‌های React به نام اپلیکیشن‌ها با صفحه منفرد نیز نامیده می‌شوند، اما شاید بپرسید معنی این اصطلاح چیست؟

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

امروزه با رواج فریمورک‌های فرانت‌اند مدرن جاوا اسکریپت مانند React، اپلیکیشن معمولاً به صورت یک اپلیکیشن تک‌صفحه‌ای ساخته می‌شود یعنی شما تنها یک کد (HTML، CSS و جاوا اسکریپت)، اپلیکیشن را بارگذاری می‌کنید و هنگامی که با اپلیکیشن تعامل پیدا می‌کنید؛ جاوا اسکریپت به طور تدریجی رویدادهای مرورگر را تفسیر می‌کند و به جای ارسال درخواست‌های جدید به سرور و بازگشت یک سند جدید از آن، داده‌هایی را به صورت JSON از سرور درخواست می‌کند و یا عملی روی سرور اجرا می‌شود؛ اما خود صفحه هرگز به طور کامل بارگذاری مجدد نمی‌شود و در فرایندی شبیه به اپلیکیشن دسکتاپ عمل می‌کند.

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

نمونه‌هایی از اپلیکیشن‌های با صفحه منفرد

برخی نمونه‌های اپلیکیشن‌های تک‌صفحه‌ای که می‌توان مورد اشاره قرار داد، به شرح زیر هستند:

  • جیمیل
  • گوگل مپ
  • فیسبوک
  • توییتر
  • گوگل درایو

مزایا و معایب اپلیکیشن‌های تک‌صفحه‌ای

عملکرد یک اپلیکیشن تک‌صفحه‌ای از نظر کاربر بسیار سریع‌تر به نظر می‌رسد، چون به جای این که منتظر ارتباط بین سرور-کلاینت بماند و همچنین صبر کند تا مرورگر صفحه را مجدداً رندر کند، می‌تواند بازخورد آنی داشته باشد. این طراحی در حوزه مسئولیت سازنده اپلیکیشن است؛ اما می‌توان از انیمیشن‌های گذار و چرخشی یا هر نوع بهبود تجربه کاربری (UX) که قطعاً بهتر از گردش کار سنتی است نیز بهره گرفت.

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

اپلیکیشن‌های تک‌صفحه‌ای به راحتی قابل تبدیل به اپلیکیشن‌های وب پیشرونده (progressive) هستند که به نوبه خود امکان کش کردن به صورت محلی و پشتیبانی از تجربه آفلاین سرویس‌ها را فراهم می‌سازند. همچنین در صورتی که نیاز باشد کاربر آنلاین شود خطایی که ارائه می‌کنند به صورت بهتری ارائه می‌شود.

اپلیکیشن‌های تک‌صفحه‌ای در مواردی استفاده می‌شوند که نیازی به سئو (بهینه‌سازی موتور جستجو) نباشد. برای نمونه در مورد اپلیکیشن‌هایی که در پشت فرم login قرار می‌گیرند از این حالت استفاده می‌شود.

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

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

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

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

مدیریت ناوبری مرورگر

از آنجا که رفتار ناوبری پیش‌فرض مرورگر در اپلیکیشن‌های تک‌صفحه‌ای بازنویسی می‌شود، URL-ها باید به صورت دستی مدیریت شوند. بخشی از این اپلیکیشن‌ها، مسیریاب یا روتر (Router) نامیده می‌شود. برخی فریمورک‌ها مانند Ember این وظیفه را بر عهده دارند و برخی دیگر مانند ری‌اکت به کتابخانه‌هایی مثل React router برای انجام این کار نیازمند هستند.

مشکل این است که در آغاز توسعه اپلیکیشن‌های تک‌صفحه‌ای، به این وضعیت که دکمه‌های ناوبری مرورگر چه تأثیری روی عملکرد اپلیکیشن دارند اندیشیده نشده بود. این امر موجب خطای مشهور «از کار افتادن دکمه بازگشت» می‌شد و در زمان ناوبری درون اپلیکیشن، URL تغییر نمی‌یافت (چون رفتار پیش‌فرض مرورگر استفاده می‌شد) و زدن دکمه بازگشت موجب می‌شد که کاربران به صفحه قبلی که بازدید کرده بودند بروند و این مورد می‌توانست وب‌سایتی باشد که مدت‌ها قبل مورد بازدید قرار گرفته بود.

این مشکل هم اینک با استفاده از History API که از سوی مرورگرها ارائه شده رفع شده است؛ اما در اغلب موارد باید از کتابخانه‌ای مانند React Router استفاده کنید که به طور داخلی از این API بهره می‌گیرد.

رویکرد اعلانی (Declarative)

زمانی که به شما گفته می‌شود ری‌اکت از رویکرد اعلانی استفاده می‌کند، معنی این گفته برای شما چیست؟ در مقالات زیادی اشاره شده است که ری‌اکت از یک رویکرد اعلانی برای ساخت رابط کاربری استفاده می‌کند.

ری‌اکت باعث شده است که «رویکرد اعلانی» بسیار رایج و مطرح شود و از این رو دنیای فرانت‌اند به وسیله آن همراه با ری‌اکت گسترش یافته است. رویکرد اعلانی عملاً مفهوم جدیدی محسوب نمی‌شود؛ اما روشی که ری‌اکت برای ساخت رابط کاربری استفاده می‌کند بسیار اعلانی‌تر از قالب‌های HTML است. به عنوان نمونه:

  • با استفاده از ری‌اکت می‌توان رابط‌های وب را بدون حتی تماس مستقیم با DOM ساخت.
  • می‌توان یک سیستم رویداد بدون نیاز به تعامل با رویدادهای DOM واقعی طراحی کرد.

متضاد رویکرد اعلانی یک رویکرد «تکراری» (Iterative) است. مثال رایج برای رویکرد تکراری به گشتن در میان عناصر DOM با استفاده از jQuery یا رویدادهای DOM می‌توان اشاره کرد.

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

تغییرناپذیری (Immutability)

یکی از مفاهیمی که احتمالاً در زمان برنامه‌نویسی در React با آن مواجه خواهید شد بحث تغییرناپذیری و متضاد آن تغییرپذیری (mutability) است. البته این یک موضوع پر حرف و حدیث است؛ اما هر دیدگاهی که در مورد مفهوم تغییرناپذیری داشته باشید؛ در هر حال ری‌اکت و غالب بخش‌های اکوسیستم آن به نوعی استفاده از این مفهوم را اجبار کرده‌اند و از این رو باید دست‌کم درکی از این مفهوم داشته باشید. به همین جهت این مفهوم و نتایج ضمنی آن مهم است.

در برنامه‌نویسی یک متغیر زمانی تغییرناپذیر نامیده می‌شود که مقدار آن پس از ایجاد متغیر نتواند عوض شود.

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

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

این مفهوم در بخش‌های مختلف ری‌اکت نیز مصداق دارد. برای نمونه، شما هرگز نباید خصوصیت state یک کامپوننت را به صورت مستقیم تغییر دهید؛ بلکه این کار باید صرفاً از طریق متد ()setState صورت بگیرد. در Redux شما هرگز نباید state را به صورت مستقیم تغییر دهید؛ بلکه این کار از طریق reducer-ها که تابع‌های تعریف شده ما هستند تغییر می‌یابند.

اگر بخواهیم دلایل این مسئله را توضیح دهیم مهم‌ترین موارد به صورت زیر هستند:

  • تغییرپذیری می‌تواند به صورت متمرکز مانند حالت redux انجام یابد که موجب بهبود قابلیت‌های دیباگ و کاهش منابع خطا می‌شود.
  • در این حالت کد تمیزتر به نظر می‌رسد و درک آن آسان‌تر است. شما هرگز انتظار ندارید که یک تابع مقداری را بدون اطلاع شما تغییر دهد، چون می‌خواهید «قابلیت پیش‌بینی» (predictability) داشته باشید. هنگامی که یک تابع شیءها را تغییر ندهد؛ بلکه یک شیء جدید بازگشت دهد، این تابع یک «تابع محض» (pure function) یا در مواردی تابع خالص نیز نامیده می‌شود.
  • کتابخانه می‌تواند موجب بهینه‌سازی کد شود، زیرا برای مثال جاوا اسکریپت زمانی که به جای تغییر شیء موجود، ارجاع یک شیء قدیمی را با شیئی کاملاً جدید عوض می‌کند، بسیار سریع‌تر می‌شود. بدین ترتیب بهبود عملکرد رخ می‌دهد.

محض بودن

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

خروجی یک تابع محض صرفاً بر اساس آرگومان‌هایش مشخص می‌شود. اگر این تابع را 1 میلیون بار نیز فراخوانی کنیم و مجموعه آرگومان‌های یکسانی به آن بدهیم، خروجی همواره همان خواهد بود. React از این مفهوم در مورد کامپوننت‌ها استفاده می‌کند. یک کامپوننت ری‌اکت زمانی یک کامپوننت محض است که خروجی آن صرفاً به props کامپوننت وابسته باشد. همه کامپوننت‌های تابعی کامپوننت‌های محض هستند:

کامپوننت‌های کلاس در صورتی که خروجی‌شان تنها به props وابسته باشد، می‌توانند کامپوننت‌های محض باشند:

ترکیب‌بندی

در برنامه‌نویسی، ترکیب‌بندی امکان ساخت کارکردهای پیچیده‌تر را از طریق ترکیب‌بندی تابع‌های کوچک و متمرکز فراهم می‌کند. برای نمونه می‌توان از ()map برای ساخت یک آرایه جدید از مجموعه اولیه‌ای استفاده کرد و سپس نتایج را با استفاده از ()filter، فیلتر کرد:

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

ایجاد نسخه سفارشی از یک کامپوننت

با گسترش و تخصصی ساختن یک کامپوننت خاص، می‌توان یک کامپوننت عمومی‌تر ساخت:

ارسال متدها به عنوان props

برای نمونه یک کامپوننت می‌تواند روی ردگیری یک رویداد کلیک متمرکز شود و آنچه در عمل رخ می‌دهد این باشد که وقتی رویداد کلیک رخ می‌دهد عمل متقابل آن بر عهده کامپوننت کانتینر گذاشته شود:

استفاده از فرزندان

خصوصیت props.children امکان تزریق کامپوننت‌ها به درون کامپوننت‌های دیگر را فراهم می‌سازد. این کامپوننت باید props.children را در JSX خود به صورت خروجی ارائه کند:

شما می‌توانید کامپوننت‌های بیشتری را به این ترتیب به روشی کاملاً شفاف جاسازی کنید:

کامپوننت‌های درجه بالا

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

DOM مجازی

بسیاری از فریمورک‌های موجود که پیش از React به صحنه آمده‌اند، به طور مستقیم DOM را در هر تغییر دستکاری می‌کنند. اگر نمی‌دانید DOM چیست، باید بگوییم که DOM یا «مدل شیء سند» (Document Object Model) یک بازنمایی درختی از صفحه است که از تگ <html> آغاز می‌شود و به سمت پایین و همه فرزندان که گره نامیده می‌شوند، حرکت می‌کند.

این DOM در حافظه مرورگر نگهداری می‌شود و مستقیماً با آنچه روی صفحه دیده می‌شود ارتباط دارد. DOM یک API دارد که می‌توان آن را پیمایش کرد و به تک تک گره‌ها دسترسی داشت و آن‌ها را فیلتر کرد یا تغییر داد.

این API ساختار آشنایی دارد و اگر قبلاً از API ارائه شده از سوی jQuery استفاده نکرده باشید نیز احتمالاً این ساختار را بارها دیده‌اید:

document.getElementById(id)
document.getElementsByTagName(name)
document.createElement(name)
parentNode.appendChild(node)
element.innerHTML
element.style.left
element.setAttribute()
element.getAttribute()
element.addEventListener()
window.content
window.onload
window.dump()
window.scrollTo()

React یک کپی از بازنمایی DOM برای آنچه «رندرینگ ری‌اکت» نامیده می‌شود نگه‌داری می‌کند.

توضیح DOM مجازی

هر بار که DOM تغییر می‌یابد، مرورگر باید دو عملیات وسیع را اجرا کند: یک عملیات «رسم مجدد» (repaint) که در طی آن محتوا یا عناصر بصری روی عنصری تغییر می‌یابند به طوری که روی طرح‌بندی و ترکیب‌بندی آن با عناصر دیگر تأثیر نمی‌گذارد و عملیات دیگر نیز reflow است که در طی آن طرح‌بندی بخشی از صفحه تغییر می‌یابد و یا این که طرح کلی صفحه عوض می‌شود.

React از DOM مجازی برای کمک به مرورگر جهت استفاده کمتر از منابع در زمان نیاز به اجرای تغییرات روی صفحه استفاده می‌کند. هنگامی که ()setState روی یک کامپوننت فراخوانی می‌شود، یک حالت متفاوت از حالت قبلی تعیین می‌شود و React کامپوننت را به صورت dirty معرفی می‌کند. دقت کنید که ری‌اکت تنها زمانی به‌روزرسانی می‌شود که یک کامپوننت، حالت (state) آن را صراحتاً تغییر دهد.

سپس اتفاق‌های زیر رخ می‌دهند:

  • ری‌اکت، DOM مجازی را در رابطه با کامپوننت‌هایی که به صورت dirty علامت‌گذاری شده‌اند به همراه برخی بررسی‌های اضافی مانند ردگیری ()shouldComponentUpdate به‌روزرسانی می‌کند.
  • الگوریتم diffing اجرا می‌شود تا تغییرات هماهنگ شوند.
  • DOM واقعی به‌روزرسانی می‌شود.

DOM مجازی با استفاده از تجمیع تغییرات چگونه مفید واقع می‌شود؟

نکته کلیدی در مورد DOM مجازی این است که اغلب تغییرات تجمیع می‌شوند و یک به‌روزرسانی یکتا روی DOM واقعی اعمال می‌شود و همه عناصری که باید عوض شوند به یک‌باره تغییر می‌یابند. بنابراین مرورگر باید عملیات repaint و reflow را اجرا کند تا تغییرات اجرا شده را به یک باره رندر کند.

گردش داده غیر جهت‌دار

هنگام کار کردن با ری‌اکت با اصطلاح گردش داده غیر جهت‌دار مواجه می‌شویم. اگر می‌خواهید معنی دقیق آن را بدانید باید بگوییم که گردش داده غیر جهت‌دار مفهومی منحصر به فرد در React محسوب نمی‌شود؛ اما توسعه‌دهندگان جاوا اسکریپت ممکن است نخستین باری باشد که آن را می‌شنوند.

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

  • حالت (State) به view و سپس کامپوننت‌های فرزند ارسال می‌شود.
  • اکشن‌ها به وسیله view تحریک می‌شوند.
  • اکشن‌ها می‌توانند state را به‌روزرسانی کنند.
  • تغییرات state به view و کامپوننت‌های فرزند آن ارسال می‌شوند.

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

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

یک state تحت مالکیت کامپوننت است. هر داده‌ای که تحت تأثیر این state قرار گیرد، می‌تواند تنها کامپوننت‌های زیرش یعنی فرزندان آن را تحت تأثیر قرار دهد. تغییر دادن state روی کامپوننت هرگز والد آن یا هم‌نیا‌ها و یا دیگر کامپوننت‌های اپلیکیشن را تحت تأثیر قرار نمی‌دهد. فقط فرزندان هستند که تأثیر می‌پذیرند.

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

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

telegram
twitter

میثم لطفی

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

بر اساس رای 1 نفر

آیا این مطلب برای شما مفید بود؟

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *