دلایل استفاده از Composition API در Vue.js 3

Reasons to use Composition API in Vue.js 3

24 شهریور 1399
دلایل استفاده از composition API در Vue.js 3

با سلام و خسته نباشید خدمت همراهان گرامی روکسو. همانطور که می دانید نسخه بتا از ورژن سوم Vue.js چند وقتی است که منتشر شده و documentation آن روی سایت رسمی Vue قرار گرفته است. این نسخه جدید با تغییرات زیادی همراه بوده است که یکی از آن ها معرفی Composition API می باشد. ما در این مقاله می خواهیم به دلایل معرفی این API و چرایی استفاده از آن بپردازیم. برای بررسی چنین موردی باید با محدودیت های Vue 2 آشنا بشویم و سپس راه حل Vue 3 برای این مشکلات را بررسی کنیم.

محدودیت های Vue 2

اگر بخواهیم با استفاده از Vue 2 یک کامپوننت بسازیم، ممکن است به سه مشکل احتمالی برخورد کنیم.

  • مشکل اول مقیاس پذیری کامپوننت ها است. هر چه کامپوننت های شما بزرگ تر شده و کدهایشان پیچیده تر شود، خوانایی آن ها دشوار تر شده و طبیعتا مدیریت آن ها (maintainability) نیز شدیدا کاهش می یابد.
  • مشکل دوم اینجاست که ما در کامپوننت های مختلف از کدهای مشابه زیادی استفاده می کنیم اما روش بهینه ای برای اشتراک گذاری این کدهای تکراری بین کامپوننت های مختلف وجود ندارد بنابراین راه صحیحی برای code reuse نخواهیم داشت.
  • مشکل سوم اینجاست که تایپ اسکریپت به خوبی در Vue 2 پشتیبانی نمی شود و قابلیت های محدودی دارد.

محدودیت اول Vue 2: عدم خوانا بودن کدها

ما در این دو مقاله می خواهیم این مشکلات را بررسی کنیم. اولین مشکل بزرگ شدن و پیچیده شدن کامپوننت ها بود که باعث عدم خوانایی و نگهداری آسان از آن ها می شد. تصور کنید که ما می خواهیم یک input جست و جو بسازیم (یک search bar ساده برای جست و جو). در حالت عادی برای ساخت این نوار جست و جو باید به شکل زیر عمل کنیم:

<script>

export default {
    data() {
        return {
            // مقدار تایپ شده توسط کاربر را در اینجا ذخیره می کنیم
        }
    },
    methods: {
        // متدی برای اجرای جست و جو
    }
}

</script>

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

<script>
export default {
    data() {
        return {
            // مقدار تایپ شده توسط کاربر را در اینجا ذخیره می کنیم
            // خصوصیتی که نتایج جست و جو را دارد
        }
    },
    methods: {
        // متدی برای اجرای جست و جو
        // متدی که نتایج را مرتب می کند
    }
}
</script>

همانطور که می بینید کد ما به دو قسمت تقسیم شده است. با اینکه هنوز کد ما شلوغ نشده است اما اگر قابلیت های دیگری مانند اضافه کردن فیلترهای جست و جو و pagination را نیز اضافه کنیم این کامپوننت بسیار شلوغ و نامنظم خواهد شد. مشکل اصلی اینجاست که قابلیت های یک برنامه در Vue 2 بر اساس component option ها (چیز هایی مثل components و data و props و methods و computed و غیره) مرتب می شوند. با این حساب قابلیت ما ممکن است بر اساس همین موارد تقسیم شود، یعنی یک کد جست و جوی ساده به 5 قسمت مختلف data و prop و methods و computed و lifecycle methods و امثال آن تقسیم شود. مشکل اصلی نیز همین است که تقسیم یک قابلیت ساده به 5 قسمت جداگانه باعث ناخوانایی کدها می شود. روش بهتر این است که هر قابلیت، قسمت مخصوص خودش را داشته باشد تا تکه های کد برای یک قابلیت خاص از هم جدا نشوند.

اینجاست که Vue 3 با Composition API وارد کار می شود و راه حلی را ارائه می دهد. Composition API به ما اجازه می دهد که هر قابلیت را در یک گروه جداگانه داشته باشیم بنابراین مثال بالا به شکل زیر در می آید:

<script>
export default {
    setup() {
        // قابلیت اول: جست و جو
        // قابلیت دوم: مرتب کردن نتایج جست و جو
    }
}
</script>

طبیعتا این کار بسیار راحت تر و خواناتر از حالت قبلی است. به عبارتی کدهای شما درون متدی به نام setup قرار می گیرند. در ضمن یادتان باشد که استفاده از Composition API کاملا اختیاری است و اگر آن را دوست ندارید می توانید از همان روش قبلی (Vue 2) برای نوشتن کامپوننت ها استفاده کنید. احتمالا با دیدن کد بالا می گویید اگر کامپوننت من حجیم باشد، متد setup بسیار بزرگ خواهد شد تا جایی که دوباره به مشکل ناخوانایی کدها برمی خوریم! در Vue 3 ما composition function می نویسیم، یعنی منطق اجرایی شما به توابع مختلفی تقسیم می شود که می توانند در یک فایل جداگانه یا در همان فایل کامپوننت شما باشند. این متدها در خارج از setup تعریف می شوند و سپس درون آن اجرا خواهند شد بنابراین فقط صدا زدن آن ها درون setup اتفاق می افتد:

<script>
export default {
    setup() {
        return {...useSearch(),...useSorting() }
    }

    function useSearch() {
        // قابلیت اول: جست و جو
    }

    function useSorting() {
        // قابلیت دوم: مرتب کردن نتایج جست و جو
    }
}
</script>

یادتان باشد که کد بالا شکل کلی استفاده از composition API را نشان می دهد و syntax (نحو) واقعی نیست. این اولین مشکلی بود که توسط composition API حل می شود. ما در Vue 2 هر قابلیت را بر اساس عملیات های منطقی تقسیم می کردیم و نتیجه پراکنده شدن منطق اصلی برنامه بود اما در Vue 3 کدها را بر اساس کارایی آن ها تقسیم می کنیم که باعث پیوستگی منظم می شود. در ضمن این مسئله به معنی این نیست که تعداد کامپوننت های ما کمتر می شود. برخی از افراد تصور می کنند که استفاده از composition API به معنی کاهش تعداد کامپوننت ها است به طوری که هر صفحه وب درون composition function های مختلف نوشته می شود اما این مسئله اصلا صحیح نیست. برای ما هنوز هم هر صفحه به کامپوننت های مختلف تقسیم می شود و composition function ها فقط منطق هر کامپوننت را تشکیل می کنند.

محدودیت دوم Vue 2: عدم وجود روشی مناسب برای استفاده مجدد از کدها

همانطور که گفتیم در Vue 2 هیچ روش مناسبی برای code reuse یا استفاده مجدد از کدها بین کامپوننت های مختلف وجود ندارد. توجه داشته باشید که code reuse (استفاده مجدد از یک کد) با code duplication (کپی کردن یا نوشتن کدهای تکراری) تفاوت دارد. code reuse یعنی به اشتراک گذاری یک کد برای استفاده در قسمت های مختلف برنامه، بنابراین یک قابلیت مثبت محسوب می شود اما code duplication یعنی تکرار بی هدف کدها در صورتی که می توانیم جلوی آن را بگیریم. طبیعتا این موضوع یک عیب بزرگ محسوب می شود. رابطه بین code reuse و code duplication برعکس است یعنی هر چه قابلیت reuse (استفاده مجدد) بالاتر باشد، میزان کدهای تکراری (duplication) کاهش می یابد.

مثلا دوباره به کد زیر توجه کنید:

<script>
export default {
    data() {
        return {
            // مقدار تایپ شده توسط کاربر را در اینجا ذخیره می کنیم
            // خصوصیتی که نتایج جست و جو را دارد
        }
    },
    methods: {
        // متدی برای اجرای جست و جو
        // متدی که نتایج را مرتب می کند
    }
}
</script>

در Vue 2 برای اینکه بتوانیم از کدهای بالا در کامپوننت های دیگر نیز استفاده کنیم سه راه وجود دارد. راه اول استفاده از mixin ها است. به عبارتی باید یک productSearchMixin و یک resultSortMixin بسازیم و سپس این دو mixin را وارد کامپوننت مورد نظر کنیم. با استفاده از Mixin ها می توانیم کدهایمان را بر اساس قابلیت دسته بندی کنیم که مثل Composition API باعث مرتب شدن کدها می شود اما ممکن است به مشکلات conflict of name (تکراری بودن نام mixin ها) برخورد کنیم. از طرف دیگر رابطه بین این mixin ها نیز مشخص نیست و اگر کسی به آن ها نگاه کند متوجه تعامل بین آن ها نخواهد شد. در نهایت، mixin ها قابلیت reusability (استفاده مجدد) صد در صد ندارند بنابراین به معنای واقعی کلمه reusable نیستند. به عبارت دیگر ما نمی توانیم از آن ها برای جست و جوی منابع دیگر استفاده کنیم؛ مثلا productSearchMixin برای جست و جوی محصولات نوشته شده است و نمی تواند در بین نظرات کاربران یا فروشنده ها یا لیستی از خریداران و غیره جست و جو انجام دهد.

روش دوم Vue 2 برای reusability استفاده از mixin factory ها است. factory ها توابعی هستند که یک نسخه شخصی سازی شده و مخصوص به یک موقعیت خاص را برمی گردانند. در این روش ابتدا factory ها را تعریف کرده و آن ها را درون کامپوننت مورد نظر وارد می کنیم. سپس برایشان یک namespace تعریف کرده و تنظیمات مربوط به شخصی سازی را به آن ها پاس می دهیم تا دقیقا mixin مورد نظر ما را برایمان تولید کند. با این کار قابلیت reusability را خواهیم داشت، همچنین رابطه بین قابلیت های مختلف ما واضح خواهد بود اما باید استاندارد های سخت گیرانه نام گذاری برای namespace ها داشته باشید تا بتوانید آن ها را تمیز و بدون مشکل طراحی کنید. همچنین اگر بخواهیم خصوصیات (property) یک mixin را ببینیم باید به تعریف آن برگشته و دوباره کدها را بررسی کنیم. همچنین mixin factory ها قابلیت ساخته شدن به صورت پویا را ندارند بنابراین در زمان runtime نمی توانیم به نمونه (instance) های آن دسترسی داشته باشیم.

روش بعدی حل این مشکل استفاده از scoped slots است. در این روش یک کامپوننت ساده search را می سازیم که props داشته و از <slot> استفاده می کند. از طرف دیگر یک کامپوننت ساده ترتیب دهی (sorting) را می سازیم. در نهایت در هنگام استفاده از این کامپوننت ها، اطلاعات مربوط به نوع جست و جو را به صورت props به کامپوننت های مورد نظر پاس می دهیم. این روش تمام مشکلات قبلی را حل می کند اما indentation (تعداد فرورفتگی کدها) را افزایش داده که خودش باعث کاهش خوانایی کد می شود. از طرف دیگر از آنجایی که تنظیمات پیکربندی به صورت props پاس داده می شوند بنابراین در کد اصلی و هنگام استفاده از کامپوننت ها نوع آن ها را مشخص می کنیم که قالب اصلی را شلوغ می کند. همچنین این prop ها منعطف نیستند و فقط در <template> در دسترس خواهند بود. از طرف دیگر در این روش نیاز به سه کامپوننت داریم (یکی برای نوار جست و جو، یکی برای مرتب کردن نتایج و دیگر برای استفاده از این دو کامپوننت)  بنابراین سرعت برنامه نیز پایین می آید.

راه حل: استفاده از Composition Function ها

این روش چهارم ما برای reuse یا استفاده مجدد از کدها است که در نسخه سوم Vue ارائه شده است. در این روش مثل روش های قبلی یک فایل برای مکانیسم جست و جو (search) و یک فایل دیگر برای مکانیسم مرتب سازی (sorting) خواهیم داشت. این دو فایل حاوی composition function های ما خواهند بود که نحو یا syntax متفاوتی نسبت به توابع عادی دارند:

export default function useSearch (getResults) {
    // کدهای مربوط به انجام جست و جو و دریافت نتایج
}

این دو فایل کامپوننت جداگانه ای نیستند بلکه حامل های composition function ها هستند. در مرحله بعدی باید این دو فایل را وارد کامپوننت مورد نظر خودتان کرده و درون متد setup از آن ها استفاده کنید:

import useSearch from '@use/search'
import useSorting from '@use/sorting'

export default {
    setup() {
        const productSearch = useSearch(.....)
        const resultSorting = useSorting(.....)
        return {productSearch, resultSorting}
    }
}

سپس می توانیم به جای ..... در کد بالا همان تنظیمات پیکربندی خودمان را ارسال کنیم. این روش نیاز به کدنویسی کمتری دارد بنابراین برنامه آنچنان شلوغ نمی شود، همچنین از همان توابع جاوا اسکریپتی و نحو آن ها استفاده می کند بنابراین نیازی به یادگیری چیز جدیدی ندارید و البته انعطاف پذیری بسیار بیشتری نسبت به scoped slots و mixin ها دارد. در نهایت به دلیل اینکه این ها فقط توابع ساده جاوا اسکریپتی هستند، ابزار های شما مانند intellisense و autocompletion و ابزار های مانند آن برایتان کار خواهند کرد و نیاز به ابزار جداگانه ای ندارید. تنها عیب این روش این است که حالا دو syntax یا نحو مختلف برای تعریف کامپوننت ها داریم: نحو عادی که در Vue 2 هم وجود داشت و استفاده از روش پیشرفته Composition API.

زمانی که نسخه سوم Vue.js از حالت beta خارج شده و ویرایش نهایی آن منتشر شود، آموزش کامل این فریم ورک محبوب را در روکسو خواهیم داشت بنابراین منتظر باشید و آن را از دست ندهید.

نویسنده شوید
دیدگاه‌های شما (2 دیدگاه)

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

مهدی پیشگوی
27 اسفند 1399
مقاله مفیدی بود ممنون

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

فرهاد روحانی مقدس
18 اسفند 1399
ممنون خیلی خوب توضیح دادید.

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