قوانین اولویت B : به شدت توصیه میشوند
این قوانین برای بهبود خوانایی و یا تجربه توسعه دهنده در اکثر پروژهها، ایجاد شده اند. اگر این قوانین را نقض کنید، کد شما هنوز اجرا خواهد شد، اما نقضها باید نادر و به خوبی توجیه شده باشند.
فایلهای کامپوننت
هرگاه یک سیستم ساخت (build system) برای ادغام فایلها موجود باشد، هر کامپوننت باید در یک فایل جداگانه قرار گیرد.
این کمک میکند تا هنگامی که نیاز به ویرایش یک کامپوننت دارید یا میخواهید نحوه استفاده از آن را بررسی کنید، با سرعت بیشتری آن کامپوننت را پیدا کنید.
بد
js
app.component('TodoList', {
// ...
})
app.component('TodoItem', {
// ...
})
خوب
components/
|- TodoList.js
|- TodoItem.js
components/
|- TodoList.vue
|- TodoItem.vue
فرمت نامگذاری کامپوننتهای تک فایلی
نام فایل کامپوننتهای تک فایلی (Single-File Components) باید همیشه به صورت PascalCase یا همیشه kebab-case باشند.
PascalCase بهترین عملکرد را در تکمیل خودکار در ویرایشگرهای کد دارد، زیرا با نحوه ارجاع به کامپوننتها در JS یا JSX و تمپلیتها تا جای ممکن هماهنگ است. با این حال، نام فایل با مخلوط حروف بزرگ و کوچک ممکن است گاهی مشکلاتی را در سیستمهای حساس به حروف بزرگ و کوچک ایجاد کند، به همین دلیل kebab-case هم کاملاً قابل قبول است.
بد
components/
|- mycomponent.vue
components/
|- myComponent.vue
خوب
components/
|- MyComponent.vue
components/
|- my-component.vue
نامگذاری کامپوننتهای پایه
کامپوننت های پایه (همچنین به عنوان presentational، dumb، یا pure components شناخته می شوند) که از استایل و قراردادهای خاص برنامه برخوردارند، همگی باید با یک پیشوند خاص شروع شوند، مانند Base
، App
یا V
.
کامپوننت های پایه، پایهگذاری برای استایل و رفتار یکنواخت را در برنامه شما فراهم میکنند. آنها امکان دارد تنها شامل موارد زیر باشند:
- عناصر HTML
- سایر کامپوننتهای پایه
- کامپوننتهای UI سوم شخص (third-party) (کامپوننتهای اضافه شده از یک کتابخانه یا فریمورک خارجی مثل vuetify)
اما آنها هیچگاه شامل یک state سراسری (برای مثال از یک Pinia store) نمیشوند.
نام آنها اغلب شامل نام عنصری میشود که در بر میگیرند (برای مثال BaseButton
, BaseTable
)، مگر آنکه هیچ عنصری برای هدف خاص آنها وجود نداشته باشد (مثل BaseIcon
که در آن از نام عنصر HTML استفاده نشده است). اگر کامپوننت های مشابهی را برای یک زمینه خاص تر بسازید، تقریباً همیشه این کامپوننت ها را به کار می برند (برای مثال BaseButton
ممکن است در ButtonSubmit
استفاده شود).
برخی از از مزایای این قرارداد:
زمانی که کامپوننت های اصلی برنامه شما در ویرایشگرها به ترتیب حروف الفبا سازماندهی میشوند، همگی در کنار هم لیست میشوند، که این امر تشخیص و شناسایی آنها را آسانتر میکند.
از آنجا که نامهای کامپوننت ها همیشه باید چندکلمهای باشند، این قرارداد از لزوم انتخاب پیشوند دلخواه برای پوشاننده های کامپوننت های ساده (برای مثال
MyButton
،VueButton
) جلوگیری می کند.از آنجا که این کامپوننت ها به طور متداول استفاده میشوند، ممکن است بخواهید آنها را به صورت سراسری اعمال کنید به جای آن که همیشه آنها را import کنید. یک پیشوند این امکان را با Webpack ممکن میسازد :
jsconst requireComponent = require.context( './src', true, /Base[A-Z]\w+\.(vue|js)$/ ) requireComponent.keys().forEach(function (fileName) { let baseComponentConfig = requireComponent(fileName) baseComponentConfig = baseComponentConfig.default || baseComponentConfig const baseComponentName = baseComponentConfig.name || fileName.replace(/^.+\//, '').replace(/\.\w+$/, '') app.component(baseComponentName, baseComponentConfig) })
بد
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
خوب
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
نامگذاری کامپوننتهای تک نمونهای
کامپوننت هایی که تنها باید یک نمونه فعال داشته باشند، باید با پیشوند The
شروع شوند تا نشان دهند که تنها میتواند یک نمونه از آن وجود داشته باشد.
این به این معنا نیست که این کامپوننت تنها در یک صفحه استفاده میشود، بلکه تنها یک بار برای هر صفحه استفاده خواهد شد. این کامپوننتها هرگز پراپها را قبول نمیکنند، زیرا بطور خاص برای برنامهی شما ساخته شدهاند نه اینکه محتوایشان در برنامهی شما هم استفاده شود. اگر نیاز به افزودن پراپ پیدا کنید، این نشانگر خوبی است که در واقع این یک کامپوننت قابل استفاده مجدد است که در حال حاضر فقط یکبار برای هر صفحه استفاده میشود.
بد
components/
|- Heading.vue
|- MySidebar.vue
خوب
components/
|- TheHeading.vue
|- TheSidebar.vue
نامگذاری کامپوننت های مرتبط
کامپوننت های فرزندی که به شدت با والد خود ارتباط دارند، باید نام والد را به عنوان پیشوند شامل شوند.
اگر دلیل وجود یک کامپوننت، فقط استفاده در بخشی از یک کامپوننت والد باشد، این رابطه باید در نام آن واضح باشد. از آنجا که ویرایشگرها معمولاً فایلها را به ترتیب الفبا مرتب میکنند، این همچنین باعث میشود تا این فایلهای مرتبط در کنار یکدیگر قرار گیرند.
ممکن است شما تمایل داشته باشید که این مسئله را با جایگذاری کامپوننتهای فرزند در پوشههایی با نام والدین خود حل کنید. به عنوان مثال :
components/
|- TodoList/
|- Item/
|- index.vue
|- Button.vue
|- index.vue
یا :
components/
|- TodoList/
|- Item/
|- Button.vue
|- Item.vue
|- TodoList.vue
این توصیه نمیشود زیرا منجر میشود به :
- فایلهای زیاد با نام مشابه، سبب ایجاد مشکل در جابجایی سریع بین فایلها در ویرایشگر میشود.
- وجود زیرپوشههای فراوان (sub-directories)، زمان مورد نیاز برای مرور کامپوننتها در نوار کناری ویرایشگر را افزایش میدهد.
بد
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
خوب
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
ترتیب کلمات در نامگذاری کامپوننت ها
نامهای کامپوننتها باید با کلمات سطح بالا (معمولاً کلمات عمومیتر) شروع شده و با کلمات توصیفی و اصلاح کننده به پایان برسند.
ممکن است برایتان جالب باشد :
"چرا ما میخواهیم نامهای کامپوننت ها را به کمترین میزان استفاده از زبان طبیعی وادار کنیم؟"
در زبان انگلیسی عادی، صفات و توصیفهای دیگر معمولاً قبل از اسمها ظاهر میشوند، در حالی که استثناها نیاز به کلمات رابط دارند. به عنوان مثال:
- Coffee with milk
- Soup of the day
- Visitor to the museum
مطمئناً میتوانید این کلمات رابط را در نامهای کامپوننتها استفاده کنید، اما ترتیب همچنان مهم است.
همچنین توجه داشته باشید که آنچه "بالاترین سطح" در نظر گرفته می شود، با برنامه شما مرتبط خواهد بود. به عنوان مثال، اپلیکیشنی را با فرم جستجو را تصور کنید. ممکن است شامل کامپوننت هایی مانند این باشد:
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
همانطور که ممکن است متوجه شوید، خیلی سخت است که ببینید کدام کامپوننتها به طور خاص مربوط به جستجو هستند. حالا بیایید کامپوننت ها را براساس این قاعده تغییر نام دهیم :
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue
از آنجا که ویرایشگرها به طور معمول فایلها را به ترتیب الفبا مرتب میکنند، اکنون تمام ارتباطات مهم بین کامپوننتها با یک نگاه آشکار میشوند.
ممکن است شما تمایل داشته باشید که این مشکل را به شیوه های مختلف حل کنید، به عنوان مثال تمام کامپوننتهای جستجو را زیر یک پوشه با نام "search" و سپس تمام کامپوننتهای تنظیمات را زیر یک پوشه با نام "settings" قرار دهید. اما ما تنها این رویکرد را در اپلیکیشنهای بسیار بزرگ (مانند 100+ کامپوننت) توصیه میکنیم، به این دلیل :
- عموماً زمان بیشتری برای پیمایش درون زیرپوشههای تو در تو نیاز است تا اینکه از طریق یک پوشه تکی با نام
components
پیمایش شوند. - تداخل نام (برای مثال، وجود چندین کامپوننت با نام
ButtonDelete.vue
) باعث میشود که پیمایش سریع به یک کامپوننت خاص در ویرایشگر کد دشوارتر شود. - تغییر ساختار دشوارتر می شود، زیرا یافتن و جایگزینی، اغلب برای به روزرسانی ارجاعات نسبی به یک کامپوننت کافی نیست.
بد
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
خوب
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
کامپوننت های خود بسته شونده
کامپوننت های بدون محتوا درون تگ html خود، باید در کامپوننت های تک فایلی (Single-File Components)، تمپلیتهای رشتهای و JSX بصورت خود بسته شونده (self-closing) باشند، اما در تمپلیتهای تعریف شده درون DOM ها هرگز.
کامپوننت هایی که خود بسته می شوند این پیام را میرسانند که نه تنها محتوایی ندارند، بلکه قرار است محتوایی نداشته باشند. این تفاوت میان یک صفحه خالی در یک کتاب و یک صفحه با برچسب "این صفحه عمدا خالی مانده است" می باشد. همچنین کد شما بدون تگهای بسته غیرضروری تمیزتر خواهد بود.
متأسفانه، HTML اجازه نمیدهد که عناصر سفارشی خودشان به صورت خودکار بسته شوند. - تنها official "void" elements این قابلیت را دارند. به همین دلیل است که این استراتژی فقط زمانی امکانپذیر است که کامپایلر تمپلیت Vue قادر به دسترسی به تمپلیت قبل از DOM باشد و سپس HTML مطابق با استاندارد DOM را ارائه دهد.
بد
template
<!-- JSX در کامپوننت های تک فایلی، تمپلیتهای رشتهای و -->
<MyComponent></MyComponent>
template
<!-- DOM در تمپلیتهای داخل -->
<my-component/>
خوب
template
<!-- JSX در کامپوننت های تک فایلی، تمپلیتهای رشتهای و -->
<MyComponent/>
template
<!-- DOM در تمپلیتهای داخل -->
<my-component></my-component>
فرمت نامگذاری کامپوننت ها در تمپلیتها
در بیشتر پروژهها، نام کامپوننت ها باید در Single-File Components و تمپلیتهای رشتهای به صورت PascalCase باشد، اما در تمپلیتهای داخل DOM به صورت kebab-case باشد.
PascalCase در مقایسه با kebab-case مزایایی دارد :
ویرایشگرها میتوانند در تمپلیتها نام کامپوننت ها را به صورت خودکار تکمیل کنند، زیرا PascalCase در جاوااسکریپت هم استفاده میشود.
<MyComponent>
(فرم PascalCase) از لحاظ بصری بیشتر از<my-component>
(فرم kebab-case) از عناصر خود HTML مجزا است، زیرا به اندازه دو کاراکتر میان آنها تفاوت وجود دارد (دو حرف بزرگ در حالت PascalCase) در حالی که در حالت kebab-case تنها یک تفاوت وجود دارد (خط فاصله).اگر شما از هرگونه عنصر غیر Vue در تمپلیت خود استفاده کنید، مانند یک کامپوننت وب، PascalCase اطمینان حاصل می کند که کامپوننت های Vue شما به صورت مجزا قابل رویت باقی میمانند.
متاسفانه، بنابر عدم حساسیت HTML به حروف بزرگ و کوچک (case insensitivity)، در تمپلیتهای داخل DOM بایستی همچنان از kebab-case استفاده نماییم.
همچنین توجه داشته باشید که اگر قبلاً روی kebab-case سرمایه گذاری زیادی کرده اید، سازگاری با قراردادهای HTML و امکان استفاده از پوشش یکسان در تمام پروژه های شما ممکن است مهمتر از مزایای ذکر شده در بالا باشد. در آن حالت ها، استفاده از kebab-case در همه جا هم قابل قبول است.
بد
template
<!-- در کامپوننت های تک فایلی و تمپلیتهای رشتهای -->
<mycomponent/>
template
<!-- در کامپوننت های تک فایلی و تمپلیتهای رشتهای -->
<myComponent/>
template
<!-- DOM در تمپلیتهای داخل -->
<MyComponent></MyComponent>
خوب
template
<!-- در کامپوننت های تک فایلی و تمپلیتهای رشتهای -->
<MyComponent/>
template
<!-- DOM در تمپلیتهای داخل -->
<my-component></my-component>
یا
template
<!-- همه جا -->
<my-component></my-component>
نامگذاری کامپوننت ها و فرمت آن در JS/JSX
نامگذاری کامپوننت ها در JS/JSX باید همواره به صورت PascalCase باشد، گرچه ممکن است آنها در داخل رشتهها در برنامههای سادهتر که تنها از کامپوننت سراسری app.component
استفاده میکنند به صورت kebab-case باشند.
در جاوااسکریپت، PascalCase قراردادی برای نامگذاری کلاسها و سازندههای نمونه اولیه (prototype constructors) میباشد - اساسا، هر چیزی که میتواند دارای نمونههای مجزا باشد. کامپوننتهای Vue هم چنین نمونههایی دارند، بنابراین منطقی است که آنها نیز از PascalCase استفاده کنند. طبق یکی از فواید گفته شده درباره آن، استفاده از PascalCase به همراه JSX (و تمپلیتها)، به خواننده های کدها اجازه میدهد که راحت تر کامپوننت ها و عناصر HTML را از هم تشخیص بدهند
اما، برای برنامههایی که تنها از کامپوننت سراسری که به وسیله app.component
تعریف میشود استفاده میکنند، ما استفاده از kebab-case را به جای PascalCase توصیه میکنیم. به دلایل زیر:
- کامپوننت های سراسری به ندرت در جاوااسکریپت ارجاع میشوند، بنابراین پیروی از یک قرارداد برای جاوااسکریپت منطقی به نظر نمیرسد.
- این برنامه ها همواره شامل تعداد زیادی تمپلیت داخل دام (in-DOM templates) هستند، جایی که kebab-case باید استفاده شود.
بد
js
app.component('myComponent', {
// ...
})
js
import myComponent from './MyComponent.vue'
js
export default {
name: 'myComponent'
// ...
}
js
export default {
name: 'my-component'
// ...
}
خوب
js
app.component('MyComponent', {
// ...
})
js
app.component('my-component', {
// ...
})
js
import MyComponent from './MyComponent.vue'
js
export default {
name: 'MyComponent'
// ...
}
نام گذاری کامل کامپوننتها
در نامگذاری کامپوننتها واژگان بصورت کامل باید به واژگان اختصاری ترجیح داده شوند.
کامل کردن خودکار کلمات در ویرایشگرها، زحمت نوشتن نامهای طولانیتر را خیلی کم میکند، در حالی که وضوحی که ایجاد میکند بسیار ارزشمند است. همواره باید از اختصارهای غیر رایج پرهیز نمود.
بد
components/
|- SdSettings.vue
|- UProfOpts.vue
خوب
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
فرمت نام گذاری prop ها
نامگذاری prop ها باید همواره با استفاده از فرمت camelCase (کلمه اول حرف کوچک، کلمه دوم حرف بزرگ) در حین تعریف کردن صورت بپذیرد. وقتی که از prop در تمپلیتهای داخل DOM استفاده می شود، آنها باید به صورت kebab-case باشند. تمپلیتهای کامپوننت های تک فایلی و JSX می توانند هم از prop ها به صورت kebab-case و هم camelCase استفاده نمایند. فرمت باید به صورت ثابت و استوار باشد. اگر شما انتخاب کردید که از prop ها به صورت camelCase استفاده کنید، اطمینان حاصل کنید که در طول برنامه خود از حالت kebab-case استفاده نمیکنید.
بد
js
const props = defineProps({
'greeting-text': String
})
template
// DOM برای تمپلیتهای داخل
<welcome-message greetingText="hi"></welcome-message>
خوب
js
const props = defineProps({
greetingText: String
})
template
// برای کامپوننت های تک فایلی، لطفا اطمینان حاصل کنید که فرمت در تمام پروژه ثابت میماند
// شما میتوانید از هر قراردادی استفاده کنید ولی ما ترکیب کردن دو نوع فرمت استایل دهی را پیشنهاد نمیکنیم
<WelcomeMessage greeting-text="hi"/>
// یا
<WelcomeMessage greetingText="hi"/>
template
// DOM برای تمپلیتهای داخل
<welcome-message greeting-text="hi"></welcome-message>
عناصر چند صفتی
عناصر (elements) با چند صفت (attribute) باید در چند خط گسترده شوند، به صورت یک attribute در هر خط.
در جاوااسکریپت، جدا کردن پراپرتیها در چند خط در آبجکتهای دارای چند پراپرتی به طور گستردهای به عنوان یک قرارداد خوب در نظر گرفته میشود، زیرا خواندن آن آسانتر است. این موضوع برای تمپلیتهای ما در Vue و JSX نیز همین گونه در نظر گرفته میشود.
بد
template
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
template
<MyComponent foo="a" bar="b" baz="c"/>
خوب
template
<img
src="https://vuejs.org/images/logo.png"
alt="Vue Logo"
>
template
<MyComponent
foo="a"
bar="b"
baz="c"
/>
استفاده از عبارات ساده در تمپلیتها
تمپلیتهای کامپوننت باید تنها شامل عبارات ساده باشند، عبارات پیچیده تر در داخل method یا پراپرتیهای computed (همان computed در Vue) نوشته میشوند.
عبارات پیچیده در تمپلیتهای شما، آنها را کمتر قابل درک می سازد. ما باید تلاش کنیم تا مقداری که باید ظاهر شود را توصیف کنیم، نه چگونگی محاسبه آن مقدار را. method یا پراپرتیهای computed کدهای ما را قابل استفاده مجدد میکند.
بد
template
{{
fullName.split(' ').map((word) => {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
خوب
template
<!-- داخل یک تمپلیت -->
{{ normalizedFullName }}
js
// عبارت پیچیده به داخل یک پراپرتی کامپیوتد منتقل شده است
const normalizedFullName = computed(() =>
fullName.value
.split(' ')
.map((word) => word[0].toUpperCase() + word.slice(1))
.join(' ')
)
پراپرتیهای computed ساده
پراپرتیهای computed پیچیده باید تا جای ممکن به تعدادی پراپرتی ساده تر تقسیم شوند.
پراپرتیهای computed ساده تر با نامگذاری بهتر:
راحت تر تست می شوند
در حالی که هر پراپرتی computed دارای تنها یک عبارت ساده، با تعداد خیلی کمی از وابستگی میباشد، نوشتن تستهایی که تأیید میکنند درست کار میکند بسیار آسانتر است.
راحت تر خوانده میشوند
ساده سازی پراپرتیهای computed شما را وادار می کند که به هر مقداری یک نام توصیفی بدهید، حتی اگر آن مقدار دوباره استفاده نشود. این عمل کار را برای سایر توسعه دهندگان (و خودتان در آینده) راحت تر میکند تا روی بخشی از کد که برایتان مهم است تمرکز کنید و بفهمید که چه اتفاقی در حال رخ دادن است.
با نیازهای در حال تغییر سازگاری بیشتری دارد
هر مقداری که میتواند نامگذاری شود، میتواند برای نمایش دادن کاربردی باشد. برای مثال، ما ممکن است که تصمیم بگیریم که پیامی را به کاربر نمایش دهیم که به او میگوید چه مقدار پول صرفه جویی کرده است. ما ممکن است همچنین تصمیم بگیریم که مالیات بر فروش را محاسبه کنیم، اما ممکن است آن را جداگانه نمایش دهیم، نه به عنوان بخشی از قیمت نهایی.
پراپرتیهای computed کوچک و متمرکز، مفروضات چگونگی استفاده از اطلاعات را کاهش میدهند، بنابراین به بازنویسی کمتری در هر تغییر داده های مورد نیاز، نیاز دارند.
بد
js
const price = computed(() => {
const basePrice = manufactureCost.value / (1 - profitMargin.value)
return basePrice - basePrice * (discountPercent.value || 0)
})
خوب
js
const basePrice = computed(
() => manufactureCost.value / (1 - profitMargin.value)
)
const discount = computed(
() => basePrice.value * (discountPercent.value || 0)
)
const finalPrice = computed(() => basePrice.value - discount.value)
مقادیر attribute با علامت نقل و قول (" ")
مقادیر attribute عناصر HTML غیرخالی باید همواره در داخل علامت نقل و قول (" ") (به صورت تک کوت یا دابل کوت، هر کدام که در کد JS داخل آن استفاده نشده باشد) استفاده شوند.
در حالی که مقادیر attribute بدون داشتن هیچ فاصله (space) نیازی به داشتن کوت در HTML ندارند، این عادت که اغلب منجر به پرهیز از فاصله ها میشود، مقادیر attribute را ناخوانا میکند.
بد
template
<input type=text>
template
<AppSidebar :style={width:sidebarWidth+'px'}>
خوب
template
<input type="text">
template
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
دستورالعملهای کوتاه نویسی
دستورالعملهای کوتاه نویسی (Directive shorthands) (:
برای :v-bind
, @
برای :v-on
برای #
برای v-slot
) باید یا همواره استفاده شده یا هیچوقت استفاده نشوند.
بد
template
<input
v-bind:value="newTodoText"
:placeholder="newTodoInstructions"
>
template
<input
v-on:input="onInput"
@focus="onFocus"
>
template
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
خوب
template
<input
:value="newTodoText"
:placeholder="newTodoInstructions"
>
template
<input
v-bind:value="newTodoText"
v-bind:placeholder="newTodoInstructions"
>
template
<input
@input="onInput"
@focus="onFocus"
>
template
<input
v-on:input="onInput"
v-on:focus="onFocus"
>
template
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
template
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>