سوالات متداول در مورد Composition API
نکته
این سوالات متداول فرض بر آشنایی قبلی با Vue - به خصوص تجربه کار با Vue 2 با استفاده از Options API - را دارد.
Composition API چیست؟
Composition API مجموعهای از APIها است که به ما اجازه میدهد کامپوننتهای Vue را با استفاده از import کردن توابع به جای تعریف آپشنها بنویسیم. در واقع یک اصطلاح کلی برای پوشش APIهای زیر است:
Reactivity API مانند
ref()
وreactive()
که به ما اجازه میدهد stateهای reactive، computed و watchers را به صورت مستقیم ایجاد کنیم.Lifecycle Hooks مانند
onMounted()
وonUnmounted()
که به ما اجازه میدهند تا از درون برنامه (کد) به چرخه حیات کامپوننت متصل شویم.Dependency Injection (تزریق وابستگی) یعنی
provide()
وinject()
که به ما اجازه میدهند هنگام استفاده از Reactivity APIs از سیستم تزریق وابستگی Vue استفاده کنیم.
Composition API یک ویژگی داخلی Vue 3 و Vue 2.7 است. برای نسخههای قدیمیتر Vue 2، از پلاگین رسمی @vue/composition-api
استفاده کنید. معمولا در Vue 3 نیز از سینتکس <script setup>
در کامپوننتهای Single-File استفاده میشود. مثال ساده یک کامپوننت با استفاده از Composition API:
vue
<script setup>
import { ref, onMounted } from 'vue'
// reactive state
const count = ref(0)
// را تغییر میدهند و باعث به روز رسانی میشوند state توابعی که
function increment() {
count.value++
}
// lifecycle hooks
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
علیرغم سبک API مبتنی بر function composition، سیستم Composition API برنامهنویسی فانکشنال نیست. Composition API بر پایه پارادایم reactivity تغییرپذیر (mutable) و دقیقگرایانه Vue است، در حالی که برنامهنویسی فانکشنال بر عدم تغییرپذیری (immutability) تأکید دارد.
اگر علاقهمند به یادگیری استفاده از Vue با Composition API هستید، میتوانید حالت API سراسری سایت را به Composition API تغییر دهید با استفاده از دکمه تاگل در بالای sidebar سمت چپ و سپس از ابتدا راهنما را دنبال کنید.
چرا Composition API؟
استفاده مجدد بهتر از منطق | Better Logic Reuse
مزیت اصلی Composition API این است که امکان استفاده مجدد clean (تمیز) و بهینه از logic (منطق) را به صورت Composable functions فراهم میکند. این تمامی محدودیتهای mixinها را حل میکند که مکانیزم اصلی استفاده مجدد از logic در Options API است.
قابلیت استفاده مجدد از logic در Composition API منجر به پروژههای community تحسینبرانگیزی مانند VueUse شده است، که مجموعهای رو به رشد از ابزارهای composable است. همچنین به عنوان مکانیزمی تمیز برای ادغام آسان سرویسها یا کتابخانههای شخص ثالث دارای state در سیستم reactivity فریمورک Vue عمل میکند، برای مثال immutable data ، state machines و RxJS.
سازماندهی انعطافپذیرتر کد
بسیاری از کاربران دوست دارند که به طور پیشفرض با Options API کد منسجمی بنویسیم: هر چیزی جای خود را بر اساس آپشن مخصوصی که زیر آن قرار دارد، پیدا میکند. با این حال، Options API محدودیتهای جدی هنگامی که کد یک کامپوننت خاص از حد آستانه پیچیدگی مشخصی فراتر میرود، ایجاد میکند. این محدودیت به ویژه در کامپوننتهایی که نیاز به مدیریت چندین موضوع منطقی (logical concerns) دارند، برجسته است که ما شاهد آن در بسیاری از برنامههای تولید شده با Vue 2 بودهایم.
به عنوان مثال کامپوننت اکسپلورر پوشهها از GUI Vue CLI را در نظر بگیرید. این کامپوننت مسئول موارد منطقی زیر است:
- پیگیری state پوشه جاری و نمایش محتوای آن
- مدیریت ناوبری (navigation) پوشه (باز کردن، بستن، بازخوانی...)
- ایجاد پوشه جدید
- تاگل کردن روی نمایش پوشههای مورد علاقه
- تاگل کردن روی نمایش پوشههای پنهان
- مدیریت تغییرات دایرکتوری کاری جاری
نسخه اصلی این کامپوننت با Options API نوشته شده بود. اگر به هر خط کد یک رنگ بر اساس موضوع منطقی که در حال مدیریت آن است، اختصاص دهیم، به این صورت به نظر میرسد:
توجه کنید که کد مربوط به یک موضوع یکسان مجبور به تقسیم شدن تحت آپشنهای مختلف در قسمتهای مختلف فایل است. در یک کامپوننت چند صد خطی، درک و ناوبری یک موضوع منطقی تکی نیازمند اسکرول مداوم بالا و پایین فایل است که آن را دشوارتر از آنچه باید باشد، میکند. علاوه بر این، اگر قصد استخراج یک موضوع به یک ابزار قابل استفاده مجدد داشته باشیم، پیدا کردن و استخراج قطعات مناسب کد از قسمتهای مختلف فایل کار زیادی میطلبد.
اینجا همان کامپوننت، قبل و بعد از بازنویسی به Composition API است:
توجه کنید که اکنون کد مربوط به یک موضوع میتواند در کنار هم گروهبندی شود: دیگر نیازی نیست هنگام کار روی یک موضوع خاص، بین بلوکهای آپشنهای مختلف جابجا شویم. علاوه بر این، اکنون میتوانیم گروهی از کد را با تلاش کمتری به یک فایل خارجی منتقل کنیم، چرا که دیگر نیازی به جابجایی کد برای استخراج آنها نیست. این کاهش اصطکاک بازنویسی، برای نگهداری طولانی مدت در کدبیسهای بزرگ حیاتی است.
Better Type Inference
در سالهای اخیر، تعداد رو به رشدی از توسعهدهندگان فرانتاند از TypeScript استفاده میکنند زیرا به ما کمک میکند تا کد استوارتری بنویسیم، تغییرات را با اطمینان بیشتری انجام دهیم و تجربه توسعه عالی با پشتیبانی IDE داشته باشیم. با این حال، Options API که در سال 2013 طراحی شده بود، بدون در نظر گرفتن type inference طراحی شده بود. ما مجبور شدیم تا برای کار کردن type inference با Options API، برخی چیزهای پیچیده و غیرمنطقی را پیادهسازی کنیم. حتی با تمام این تلاشها، type inference برای Options API همچنان میتواند برای mixinها و تزریق وابستگی شکست بخورد.
این موضوع باعث شده بود که بسیاری از توسعهدهندگانی که میخواستند از Vue با TS استفاده کنند، بیشتر به سمت API مبتنی بر کلاس با vue-class-component
متمایل شوند. با این حال، API مبتنی بر کلاس به شدت به ES decorators تکیه دارد، language feature که در زمان توسعه Vue 3 در سال 2019 فقط یک پیشنهاد مرحله 2 بود. از اینکه API رسمی را بر پایه یک پیشنهاد ناپایدار بنا کنیم، احساس خطر کردیم. از آن زمان، پیشنهاد decorators یک بازنگری کامل دیگر را پشت سر گذاشته و در نهایت در سال 2022 به مرحله 3 رسیده است. علاوه بر این، API مبتنی بر کلاس از محدودیتهای مشابهی در بازاستفاده از منطق و سازماندهی کد همانند Options API رنج میبرد.
در مقایسه، Composition API عمدتاً از متغیرها و توابع ساده استفاده میکند که به طور طبیعی سازگار با type ها هستند. کد نوشته شده در Composition API میتواند از type inference کامل با نیاز کمینه به توضیحات manual type بهرهمند شود. اکثر مواقع، کد Composition API در TypeScript و JavaScript ساده تقریباً یکسان به نظر میرسد. این امر همچنین باعث میشود که کاربران JavaScript ساده نیز بتوانند از type inference جزئی بهرهمند شوند.
باندل پروداکشن کوچکتر و سربار کمتر | Smaller Production Bundle and Less Overhead
کد نوشته شده در Composition API و <script setup>
همچنین نسبت به معادل Options API بهینهتر و سازگارتر با minification است. این به این دلیل است که تمپلیت در یک کامپوننت <script setup>
به عنوان تابعی درون خطی در همان scope کد <script setup>
کامپایل میشود. بر خلاف دسترسی به پراپرتی با استفاده this
، کد کامپایل شده تمپلیت میتواند بدون واسطه یک نمونه پراکسی، مستقیماً به متغیرهای اعلام شده داخل <script setup>
دسترسی داشته باشد. این همچنین منجر به minification بهتر میشود زیرا نامهای متغیرها را میتوان با خیال راحت کوتاه کرد.
ارتباط با Options API
مزایا و معایب | Trade-offs
برخی از کاربرانی که از Options API به Composition API مهاجرت کردهاند، کد Composition API خود را کمتر منظم یافتهاند و نتیجه گرفتهاند که Composition API از نظر سازماندهی کد "بدتر" است. به کاربرانی با این دیدگاه توصیه میکنیم که این مسئله را از دیدگاه متفاوتی ببینند.
درست است که Composition API دیگر محافظی (guard rails) که شما را هدایت کند تا کد خود را در قسمتهای مربوطه قرار دهید، فراهم نمیکند. در عوض، شما میتوانید کد کامپوننت را مانند نوشتن JavaScript عادی بنویسید. این بدان معناست که شما میتوانید و باید خودتان بهترین روش برای سازماندهی کد Composition API را انتخاب کنید مانند نوشتن JavaScript عادی عمل کنید. اگر میتوانید JavaScript خوب مرتب شده بنویسید، باید قادر به نوشتن کد Composition API تمیز نیز باشید.
Options API اجازه میدهد بدون فکر کردن زیاد کد کامپوننتی را بنویسید که بسیاری از کاربران آن را دوست دارند. با این حال، همین الگوی ثابت سازماندهی کد میتواند مشکلساز باشد. وقتی پروژه بزرگ میشود، ممکن است بخواهید کد را refactor کنید یا سازماندهی آن را بهبود دهید. اما چون کد در Options API در بخشهای ثابتی قفل شده، refactoring و بهبود سازماندهی دشوار میشود. در این زمینه، Composition API قابلیت مقیاسپذیری بلندمدت بهتری فراهم میکند.
آیا Composition API همه موارد استفاده را پوشش میدهد؟
بله، از نظر stateful logic. هنگام استفاده از Composition API، تنها چند گزینه ممکن است همچنان نیاز باشد: props
، emits
، name
، و inheritAttrs
.
نکته
از نسخه 3.3 میتوانید مستقیماً از defineOptions
در <script setup>
برای تنظیم نام کامپوننت یا خاصیت inheritAttrs
استفاده کنید.
اگر قصد دارید فقط از Composition API (همراه با گزینههای فهرست شده بالا) استفاده کنید، میتوانید چند کیلوبایت از باندل تولیدی خود را از طریق یک compile-time flag که کد مربوط به Options API را از Vue حذف میکند، کاهش دهید. توجه داشته باشید این همچنین کامپوننتهای Vue در وابستگیهای شما (اشاره به کتابخانهها) را تحت تاثیر قرار میدهد.
آیا میتوانم از هر دو API در یک کامپوننت استفاده کنم؟
بله. میتوانید از Composition API از طریق گزینه setup()
در یک کامپوننت Options API استفاده کنید.
با این حال، ما تنها در صورتی این کار را توصیه میکنیم که یک کدبیس Options API موجود داشته باشید که نیاز به ادغام با ویژگیهای جدید / کتابخانههای خارجی نوشته شده با Composition API دارد.
آیا Options API منسوخ خواهد شد؟
خیر، ما هیچ برنامهای برای انجام این کار نداریم. Options API بخش مهمی از Vue است و دلیلی که بسیاری از توسعهدهندگان آن را دوست دارند. همچنین متوجه شدهایم که بسیاری از مزایای Composition API تنها در پروژههای مقیاس بزرگتر آشکار میشوند و Options API همچنان انتخاب مناسبی برای بسیاری از سناریوهای پیچیدگی پایین تا متوسط است.
ارتباط با Class API
دیگر توصیه نمیکنیم از Class API با Vue 3 استفاده کنید، با توجه به اینکه Composition API ادغام عالی TypeScript با مزایای اضافی استفاده مجدد از منطق و سازماندهی کد را فراهم میکند.
مقایسه با هوکهای ریاکت
Composition API همان سطح از قابلیتهای ترکیب logic هوکهای ریاکت را فراهم میکند، اما با برخی تفاوتهای مهم.
هوکهای ریاکت به طور مکرر هر بار که یک کامپوننت بهروزرسانی میشود، فراخوانی میشوند. این موضوع باعث ایجاد چندین محدودیت میشود که حتی توسعهدهندگان باتجربه ریاکت را گیج میکند. همچنین منجر به مسائل performance optimization (بهینهسازی عملکرد) میشود که میتواند تجربه توسعه را به شدت تحت تأثیر قرار دهد. چند مثال:
هوکها به ترتیب فراخوانی حساس هستند و نمیتوانند شرطی باشند.
متغیرهای اعلام شده در یک کامپوننت ریاکت میتوانند توسط یک کلوژر هوک capture شوند و اگر توسعهدهنده آرایه وابستگیهای صحیح را ارسال نکند، "stale" شوند. این موضوع منجر میشود توسعهدهندگان ریاکت به قوانین ESLint برای اطمینان از ارسال وابستگیهای صحیح تکیه کنند. با این حال، اغلب اوقات این قاعده به اندازه کافی هوشمند نیست و برای صحت بیش از حد سختگیری میکند که منجر به ابطال غیرضروری و سردرگمی هنگام مواجهه با موارد لبهای میشود.
محاسبات پرهزینه نیاز به استفاده از
useMemo
دارند که دوباره نیاز به ارسال درست دستی آرایه وابستگی دارد.از آنجا که event handler های ارسال شده به کامپوننتهای فرزند بهطور پیشفرض باعث بهروزرسانیهای غیرضروری فرزند میشوند، نیاز به
useCallback
صریح به عنوان بهینهسازی وجود دارد. این تقریباً همیشه لازم است و دوباره نیاز به آرایه وابستگیهای صحیح دارد. غفلت از این موضوع بهطور پیشفرض منجر به بیشرندر کردن برنامهها میشود و میتواند بدون متوجه شدن منجر به مشکلات عملکردی (performance issues) شود.مشکل کلوژر کهنه، در ترکیب با ویژگیهای همزمان، باعث میشود استدلال در مورد زمان اجرای یک قطعه کد هوک دشوار شود و کار با state قابل تغییری که باید در طول رندرها ماندگار باشد (از طریق
useRef
) پیچیده میشود.
توجه: برخی از مسائل فوق که به حافظهسازی مربوط میشوند، میتوانند با کامپایلر React حل شوند.
در مقابل، Composition API Vue:
setup()
یا<script setup>
را فقط یک بار فراخوانی میکند. این باعث میشود کد بهتر با شهود برنامهنویسی جاوااسکریپت ایدیوماتیک همراستا شود زیرا نگرانی در مورد کلوژرهای کهنه وجود ندارد. فراخوانیهای Composition API همچنین به ترتیب فراخوانی حساس نیستند و میتوانند شرطی باشند.سیستم reactivity رانتایم Vue به طور خودکار وابستگیهای reactive مورد استفاده در پراپرتیهای computed و watchers را جمعآوری میکند، بنابراین نیازی به اعلام دستی وابستگیها نیست.
نیازی به کش کردن دستی توابع callback برای جلوگیری از بهروزرسانیهای غیرضروری فرزندان وجود ندارد. بهطور کلی، سیستم reactivity دقیقگرایانه Vue اطمینان میدهد کامپوننتهای فرزند فقط زمانی که نیاز است بهروزرسانی شوند. بهینهسازیهای بهروزرسانی فرزندان به ندرت برای توسعهدهندگان Vue نگرانکننده است.
ما خلاقیت هوکهای ریاکت را تأیید میکنیم و این یک منبع الهام عمده برای Composition API بوده است. با این حال، مسائل ذکر شده در طراحی آن وجود دارد و متوجه شدیم مدل reactivity فریمورک Vue راهی برای دور زدن آنها فراهم میکند.