Рекомендации
Это официальное руководство по стилю для Vue-специфичного кода. Если вы используете Vue в проекте, это отличная возможность избежать ошибок и антипаттернов. Однако мы не считаем, что любое подобное руководство идеально подходит для всех команд или проектов, поэтому разумные отклонения поощряются на основе полученного опыта, окружающего технологического стека и личных ценностей.
В большинстве случаев мы также будем избегать общих рекомендаций о JavaScript или HTML. Мы не против, используете ли вы точки с запятой или висячие запятые. Мы не против, используются ли в вашем HTML одинарные или двойные кавычки для значений атрибутов. Однако некоторые исключения будут присутствовать для тех случаев, где мы нашли конкретный шаблон полезным в контексте Vue.
Скоро мы предоставим рекомендации по соблюдению единого стиля кода. Иногда вам нужно просто быть дисциплинированным, но там, где это возможно, мы постараемся показать, как можно использовать ESLint и другие автоматизированные процессы, чтобы упростить соблюдение единого стиля.
Наконец, мы разделили правила на четыре категории:
Категории правил
Приоритет A: Важно
Эти правила помогут предотвратить ошибки, поэтому изучите и соблюдайте их любой ценой. Исключения могут быть, но должны быть очень редкими и должны выполняться только теми, у кого есть хорошие знания как JavaScript, так и Vue.
Приоритет B: Настоятельно рекомендуется
Эти правила помогут улучшить читаемость и/или опыт разработки в большинстве проектов. Ваш код всё равно будет работать, если вы нарушите их, но нарушения должны быть редкими и обоснованными.
Приоритет C: Рекомендуется
Где существует множество одинаково хороших вариантов, можно сделать собственный выбор для обеспечения консистентности. В этих правилах мы описываем каждый приемлемый вариант и предлагаем выбор по умолчанию. Это означает, что вы можете свободно делать другой выбор в вашей собственной кодовой базе, если вы придерживаетесь консистентности и имеете вескую причину. Пожалуйста, не стесняйтесь! Приспосабливаясь к стандартам сообщества, вы будете:
- тренировать свой мозг, чтобы легче разбираться в большинстве кода сообщества, с которым придётся столкнуться
- иметь возможность копировать и использовать большинство примеров кода сообщества без изменений
- чаще находить новых сотрудников, уже знакомых с предпочитаемым стилем кода, по крайней мере, в отношении Vue
Приоритет D: Используйте с осторожностью
Некоторые возможности Vue существуют для приспосабливания к редким крайним случаям или для обеспечения более плавной миграции старой кодовой базы. Однако при чрезмерном использовании они сделают ваш код более сложным в поддержке или могут стать источником ошибок. Эти правила освещают потенциально опасные функции, объясняя, когда и почему их следует избегать.
Правила приоритета A: Важно (Предотвращение ошибок)
Имена компонентов из нескольких слов важно
Имена компонентов должны всегда состоять из нескольких слов, за исключением корневого компонента App
и встроенных компонентов самого Vue, например, <transition>
или <component>
.
Это предотвращает конфликты с существующими или будущими HTML-элементами, поскольку все HTML-элементы именуются одним словом.
Данные компонента важно
Свойство data
компонента должно быть функцией.
При использовании свойства data
в компоненте (т.е. везде, за исключением new Vue
), значением всегда должна быть функция, возвращающая объект.
Подробное объяснение
Когда значением data
будет объект, он будет использован для всех экземпляров компонента. Представьте, например, компонент TodoList
с таким набором данных:
data: {
listTitle: '',
todos: []
}
Мы можем захотеть повторно использовать этот компонент, позволяя пользователям использовать несколько списков (например, для покупок, списков желаний, ежедневных дел и т.п.). Однако есть проблема. Поскольку каждый экземпляр компонента ссылается на один и тот же объект данных, изменение названия одного списка также изменит заголовок всех остальных списков. То же самое случится и при добавлении/изменении/удалении элемента списка.
Вместо этого мы хотим, чтобы каждый экземпляр компонента управлял только своими собственными данными. Чтобы этого добиться, каждый экземпляр должен сгенерировать собственный уникальный объект данных. В JavaScript этого возможно достигнуть, возвращая объект из функции:
data: function () {
return {
listTitle: '',
todos: []
}
}
Хорошо
Vue.component('some-comp', {
data: function() {
return {
foo: 'bar'
}
}
})
// В .vue-файле
export default {
data() {
return {
foo: 'bar'
}
}
}
// Допускается использовать объект напрямую
// в корневом экземпляре Vue, поскольку только
// один экземпляр будет существовать.
new Vue({
data: {
foo: 'bar'
}
})
Определение входных параметров важно
Входные параметры должны быть определены как можно более подробно.
В готовом коде определение входных параметров всегда должно быть максимально подробным, по крайней мере определяя тип данных.
Подробное объяснение
Подробное определение входных параметров имеет два преимущества:
- Документирование API компонента, что позволит легко понимать, как этот компонент должен использоваться.
- При разработке, Vue будет предупреждать вас, если компоненту были переданы входные параметры неправильного формата, позволяя вам отловить и исправить потенциальные источники ошибок.
Хорошо
props: {
status: String
}
// Ещё лучше!
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
'syncing',
'synced',
'version-conflict',
'error'
].indexOf(value) !== -1
}
}
}
Уникальные ключи для v-for
важно
Всегда используйте key
с v-for
.
key
с v-for
всегда обязателен для компонентов, для поддержания внутреннего состояния компонента и его поддерева. Даже для элементов это хорошая практика для поддержания предсказуемого поведения, такого как консистентности объекта в анимации.
Подробное объяснение
Представим, что у нас есть список различных todo:
data: function () {
return {
todos: [
{
id: 1,
text: 'Изучить, как использовать v-for'
},
{
id: 2,
text: 'Изучить, как использовать key'
}
]
}
}
Затем вы сортируете их по алфавиту. При обновлении DOM, Vue будет оптимизировать отрисовку для выполнения самых дешёвых изменений DOM. Это может означать удаление первого элемента списка, а затем добавление его снова в конце списка.
Проблема в том, что бывают случаи, когда важно не удалять элементы, которые останутся в DOM. Например, вы можете использовать <transition-group>
для анимации сортировки списка, или удержании фокуса, если отображаемый элемент является <input>
. В этих случаях добавление уникального ключа для каждого элемента (например, :key="todo.id"
) подскажет Vue, как вести себя более предсказуемо.
По нашему опыту, лучше всегда добавлять уникальный ключ, чтобы вам и вашей команде никогда не приходилось беспокоиться об этих крайних случаях. Затем в редких критически зависимых от производительности случаях, когда консистентность объекта не требуется — вы можете сделать сознательное исключение.
Избегайте использования v-if
с v-for
важно
Никогда не используйте v-if
на том же элементе, что и v-for
.
Есть два распространённых случая, когда это может быть заманчиво:
Чтобы фильтровать элементы списка (например,
v-for="user in users" v-if="user.isActive"
). В этих случаях заменитеusers
новым вычисляемым свойством, которое возвращает ваш отфильтрованный список (например,activeUsers
).Чтобы избежать отображения списка, если он должен быть скрыт (например,
v-for="user in users" v-if="shouldShowUsers"
). В этих случаях переместитеv-if
выше, в элемент контейнера (например,ul
,ol
).
Подробное объяснение
Когда Vue обрабатывает директивы, v-for
имеет более высокий приоритет, чем v-if
, поэтому такой шаблон:
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
Будет аналогичен подобному:
this.users.map(function(user) {
if (user.isActive) {
return user.name
}
})
Таким образом, даже если мы показываем элементы только для небольшой части пользователей, мы должны перебирать весь список при каждом повторной отрисовке, независимо от того, изменился ли набор активных пользователей.
Заменив это отображением вычисляемого свойства, подобным такому:
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
Мы получаем следующие преимущества:
- Отфильтрованный список будет вычисляться повторно только при изменениях в массиве
users
, что делает фильтрацию более эффективной. - Используя
v-for="user in activeUsers"
, на этапе отрисовки мы итерируем только активных пользователей, что сделает отрисовку намного более эффективным. - Логика теперь отделена от уровня представления, что значительно упростит дальнейшую поддержку кода (изменение/расширение логики).
Мы получаем такие же преимущества обновив подобное:
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
до такого:
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
Перемещая v-if
в элемент контейнера, мы больше не проверяем shouldShowUsers
для каждого пользователя в списке. Вместо этого мы проверяем его один раз и даже не выполняем v-for
если значение shouldShowUsers
будет false.
Плохо
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
Хорошо
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
Локальные стили компонента важно
Для приложений стили в корневом компоненте App
и в компонентах шаблона могут быть глобальными, но во всех остальных компонентах должны быть локальными.
Это относится только к однофайловым компонентам. Это не требует использования атрибута scoped
. Область действия стилей может ограничиваться через CSS-модули, стратегию на основе именования классов, такой как БЭМ, или другой библиотекой/соглашением.
Однако библиотеки компонентов должны предпочитать использовать стратегию на основе именования классов вместо использования атрибута scoped
.
Это упрощает переопределение внутренних стилей с использованием читаемых названий классов, которые не имеют слишком высокой специфичности, но всё же вряд ли приведут к конфликту.
Подробное объяснение
Если вы разрабатываете большой проект, работая совместно с другими разработчиками или иногда используете сторонний HTML/CSS (например, от Auth0), консистентное ограничение области позволит гарантировать, что ваши стили применяются только к компонентам, для которых они предназначены.
Помимо атрибута scoped
, использование уникальных имён классов может помочь гарантировать, что сторонний CSS не применяется к вашему собственному HTML. Например, многие проекты используют классы button
, btn
или icon
, поэтому даже если вы не используете стратегию, такую как БЭМ, то добавление префикса приложения и/или компонента (например, ButtonClose-icon
) может обеспечить некоторую защиту.
Плохо
<template>
<button class="btn btn-close">X</button>
</template>
<style>
.btn-close {
background-color: red;
}
</style>
Хорошо
<template>
<button class="button button-close">X</button>
</template>
<!-- Использование атрибута `scoped` -->
<style scoped>
.button {
border: none;
border-radius: 2px;
}
.button-close {
background-color: red;
}
</style>
<template>
<button :class="[$style.button, $style.buttonClose]">X</button>
</template>
<!-- Использование CSS-модулей -->
<style module>
.button {
border: none;
border-radius: 2px;
}
.buttonClose {
background-color: red;
}
</style>
<template>
<button class="c-Button c-Button--close">X</button>
</template>
<!-- Использование методологии БЭМ -->
<style>
.c-Button {
border: none;
border-radius: 2px;
}
.c-Button--close {
background-color: red;
}
</style>
Именование приватных свойств важно
Используйте область видимости модуля, чтобы приватные функции были недоступны извне. Если это невозможно, всегда используйте префикс $_
для пользовательских приватных свойств в плагине, примеси и т.п. Таким образом, они не рассматриваться как публичный API-интерфейс. Затем, чтобы избежать конфликтов с кодом других авторов, также включайте именованную область (например, $_yourPluginName_
).
Подробное объяснение
Vue использует префикс _
для определения собственных приватных свойств, поэтому использование одного и того же префикса (например, _update
) может привести к перезаписи свойства экземпляра. Даже если вы проверяете и Vue в настоящее время не использует определённое имя свойства, нет гарантий, что конфликт не возникнет в более поздних версиях.
Что касается префикса $
, то в рамках экосистемы Vue это специальные свойства экземпляра, которые публично доступны пользователю, поэтому использование его для приватных свойств было бы нецелесообразным.
Вместо этого мы рекомендуем комбинировать два префикса в $_
, как соглашение для пользовательских приватных свойств, которые гарантируют отсутствие конфликтов с Vue.
Плохо
var myGreatMixin = {
// ...
methods: {
update: function() {
// ...
}
}
}
var myGreatMixin = {
// ...
methods: {
_update: function() {
// ...
}
}
}
var myGreatMixin = {
// ...
methods: {
$update: function() {
// ...
}
}
}
var myGreatMixin = {
// ...
methods: {
$_update: function() {
// ...
}
}
}
Хорошо
var myGreatMixin = {
// ...
methods: {
$_myGreatMixin_update: function() {
// ...
}
}
}
// Ещё лучше!
var myGreatMixin = {
// ...
methods: {
publicMethod() {
// ...
myPrivateFunction()
}
}
}
function myPrivateFunction() {
// ...
}
export default myGreatMixin
Правила приоритета B: Настоятельно рекомендуется (Улучшение читаемости)
Файлы компонентов настоятельно рекомендуется
Всякий раз, когда система сборки позволяет конкатенировать файлы, каждый компонент должен быть в собственном файле.
Это поможет вам быстрее найти компонент, когда потребуется его отредактировать или просмотреть как его использовать.
Именование однофайловых компонентов настоятельно рекомендуется
Имена файлов однофайловых компонентов должны быть всегда в PascalCase или всегда в kebab-case.
PascalCase лучше всего работает с автодополнением в редакторах кода, поскольку он согласуется с тем, как мы ссылаемся на компоненты в JS(X) и шаблонах. Тем не менее, смешанные имена файлов иногда могут создавать проблемы для нечувствительных к регистру файловых систем, поэтому kebab-case также вполне приемлем.
Именование базовых компонентов настоятельно рекомендуется
Базовые компоненты (известные как презентационные, глупые или чистые компоненты) которые применяют специфичные для вашего приложения стили или соглашения должны начинаться с определённого префикса, такого как Base
, App
или V
.
Подробное объяснение
Эти компоненты закладывают основу для консистентности стилей и поведения в вашем приложении. Они могут содержать только:
- HTML-элементы,
- другие базовые компоненты
- сторонние UI-компоненты.
Но они никогда не содержат глобальное состояние (например, из хранилища Vuex).
Их имена зачастую содержат название элемента, который они оборачивают (например, BaseButton
, BaseTable
), если не существует элемента для этих конкретных целей (например, BaseIcon
). Если вы создадите похожие компоненты для более специфичного контекста, они почти всегда будут поглощать эти компоненты (например, BaseButton
может использоваться в ButtonSubmit
).
Некоторые преимущества этого соглашения:
Когда они организованы в алфавитном порядке в редакторе, базовые компоненты вашего приложения будут перечислены вместе, что упрощает их идентификацию.
Поскольку имена компонентов всегда должны состоять из нескольких слов, это соглашение запрещает вам выбирать произвольный префикс для простых компонентов-обёрток (например,
MyButton
,VueButton
).Поскольку эти компоненты часто используются, вы можете просто сделать их глобальными, а не импортировать их повсюду. Префикс делает это возможным с помощью Webpack:
var requireComponent = require.context('./src', true, /Base[A-Z]\w+\.(vue|js)$/) requireComponent.keys().forEach(function (fileName) { var baseComponentConfig = requireComponent(fileName) baseComponentConfig = baseComponentConfig.default || baseComponentConfig var baseComponentName = baseComponentConfig.name || ( fileName .replace(/^.+\//, '') .replace(/\.\w+$/, '') ) Vue.component(baseComponentName, baseComponentConfig) })
Хорошо
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
Именование компонентов, используемых в единственном экземпляре настоятельно рекомендуется
Компоненты, которые должны иметь только один активный экземпляр, следует начинать именовать с префикса The
, обозначая таким образом что он может быть только один.
Это не означает, что компонент используется только на одной странице, но означает что он будет использоваться только один раз на странице. Эти компоненты никогда не принимают каких-либо входных параметров, поскольку они специфичны для вашего приложения, а не их контекста в вашем приложении. Если вы обнаружите необходимость добавления входных параметров, это хороший признак того, что на самом деле этот компонент для многократного использования, который используется только один раз на странице в данный момент.
Именование тесно связанных компонентов настоятельно рекомендуется
Дочерние компоненты, тесно связанные с родителями, должны включать имя родительского компонента в качестве префикса.
Если компонент имеет смысл только в контексте одного родительского компонента, то это отношение должно быть очевидным в его имени. Поскольку редакторы обычно упорядочивают файлы по алфавиту, это также расположит связанные файлы друг с другом.
Подробное объяснение
Возможно вы захотите решить эту проблему, вложив дочерние компоненты в каталоги, названные в честь их родителя. Например:
components/
|- TodoList/
|- Item/
|- index.vue
|- Button.vue
|- index.vue
или:
components/
|- TodoList/
|- Item/
|- Button.vue
|- Item.vue
|- TodoList.vue
Это не рекомендуется, так как это приводит к:
- Множеству файлов с похожими именами, что затрудняет быстрое переключение между файлами в редакторе кода.
- Множеству подкаталогов, что увеличивает время необходимое на изучение списка компонентов в боковой панели редактора.
Плохо
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
Самозакрывающиеся теги компонентов настоятельно рекомендуется
Компоненты без содержимого должны быть самозакрывающимися тегами в однофайловых компонентах, строковых шаблонах и JSX — но никогда в DOM-шаблонах.
Самозакрывающиеся теги компонентов сообщают, что у них не только нет содержимого, но и сообщает что и не должны иметь содержимого. Это разница между пустой страницей в книге и страницей с надписью: «Эта страница намеренно оставлена пустой». Ваш код также станет более лаконичным без ненужного закрывающего тега.
К сожалению, HTML не разрешает пользовательским элементам быть самозакрывающимися — только официальные «void» элементы. Вот почему эта стратегия возможна только тогда, когда компилятор шаблонов Vue может достичь шаблона перед DOM, а затем предоставить DOM-совместимый HTML.
Плохо
<!-- В однофайловых компонентах, строковых шаблонах и JSX -->
<MyComponent></MyComponent>
<!-- В DOM-шаблонах -->
<my-component/>
Хорошо
<!-- В однофайловых компонентах, строковых шаблонах и JSX -->
<MyComponent/>
<!-- В DOM-шаблонах -->
<my-component></my-component>
Стиль именования компонентов в шаблонах настоятельно рекомендуется
В большинстве проектов имена компонентов всегда должны быть в PascalCase в однофайловых компонентах и строковых шаблонах — но в kebab-case в случае DOM-шаблонов.
PascalCase имеет следующие преимущества перед kebab-case:
- Редакторы могут автодополнять имена компонентов в шаблонах, потому что PascalCase также используется в JavaScript.
<MyComponent>
более визуально отличается от элемента HTML из одного слова, нежели<my-component>
, потому что есть две заметных разницы в символах (две заглавных), а не только одна (дефис).- Если вы используете какие-либо пользовательские элементы, отличные от Vue, в ваших шаблонах, например, веб-компонент, PascalCase гарантирует, что ваши компоненты Vue остаются отчётливо видимыми.
К сожалению, из-за нечувствительности HTML к регистру, DOM-шаблоны должны по-прежнему использовать kebab-case.
Также обратите внимание, что если вы уже вложили значительные силы в kebab-case, консистентность с соглашениями HTML и возможность использования такого же написания во всех ваших проектах, то это может быть более важным, чем преимущества, перечисленные выше. В этих случаях допускается использовать kebab-case повсюду.
Плохо
<!-- В однофайловых компонентах и строковых шаблонах -->
<mycomponent/>
<!-- В однофайловых компонентах и строковых шаблонах -->
<myComponent/>
<!-- В DOM-шаблонах -->
<MyComponent></MyComponent>
Хорошо
<!-- В однофайловых компонентах и строковых шаблонах -->
<MyComponent/>
<!-- В DOM-шаблонах -->
<my-component></my-component>
ИЛИ
<!-- Везде -->
<my-component></my-component>
Стиль именования компонентов в JS/JSX настоятельно рекомендуется
Стиль именования компонентов в JS/JSX всегда должен быть PascalCase, хотя они могут быть в kebab-case внутри строк для простых приложений, которые используют только глобальную регистрацию компонентов через Vue.component
.
Подробное объяснение
В JavaScript PascalCase — это соглашение для классов и конструкторов прототипов — по существу всё, что может иметь разные экземпляры. Компоненты Vue также могут иметь экземпляры, поэтому также имеет смысл использовать PascalCase. В качестве дополнительного преимущества, использование PascalCase в JSX (и шаблонах) позволяет изучающим код легче различать компоненты от HTML-элементов.
Однако, для приложений, которые используют только глобальные определения компонентов через Vue.component
, мы рекомендуем вместо него использовать kebab-case. Причины:
- Редкость, когда на глобальные компоненты ссылаются в JavaScript, поэтому следование соглашению для JavaScript имеет меньше смысла.
- Эти приложения всегда включают в себе множество шаблонов внутри DOM, где kebab-case должен быть использован.
Плохо
Vue.component('myComponent', {
// ...
})
import myComponent from './MyComponent.vue'
export default {
name: 'myComponent'
// ...
}
export default {
name: 'my-component'
// ...
}
Хорошо
Vue.component('MyComponent', {
// ...
})
Vue.component('my-component', {
// ...
})
import MyComponent from './MyComponent.vue'
export default {
name: 'MyComponent'
// ...
}
Использование полных слов при именовании компонентов настоятельно рекомендуется
Имена компонентов должны состоять из полных слов, а не аббревиатур.
Автодополнение в редакторах уменьшают сложность написания более длинных имён, а ясность, которую они предоставляют неоценима. Малоизвестных аббревиатур, в частности, следует избегать.
Стиль именования входных параметров настоятельно рекомендуется
Входные параметры должны всегда использовать camelCase при определении, но kebab-case в шаблонах и JSX.
Мы просто придерживаемся соглашений каждого языка. Для JavaScript использовать camelCase является более естественным. Для HTML — kebab-case.
Элементы с несколькими атрибутами настоятельно рекомендуется
Элементы с несколькими атрибутами должны располагаться на нескольких строках, по одному атрибуту на строку.
В JavaScript написание объектов с несколькими свойствами в несколько строк считается хорошей практикой, потому что при таком написании её гораздо легче читать. Наши шаблоны и JSX стоит рассматривать также.
Плохо
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent foo="a" bar="b" baz="c"/>
Хорошо
<img
src="https://vuejs.org/images/logo.png"
alt="Vue Logo"
>
<MyComponent
foo="a"
bar="b"
baz="c"
/>
Простые выражения в шаблонах настоятельно рекомендуется
Шаблоны компонентов должны содержать только простые выражения, а более комплексные должны быть вынесены в вычисляемые свойства или методы.
Сложные выражения в ваших шаблонах делают их менее декларативными. Мы должны стремиться к описанию что должно отобразиться, а не как мы вычисляем это значение. Вычисляемые свойства и методы также упрощают переиспользование кода.
Плохо
{{
fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
Хорошо
<!-- В шаблоне -->
{{ normalizedFullName }}
// Комплексное выражение было вынесено в вычисляемое свойство
computed: {
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}
Простые вычисляемые свойства настоятельно рекомендуется
Комплексные вычисляемые свойства должны быть разделены на максимально простые свойства.
Подробное объяснение
Проще говоря, хорошие вычисляемые свойства будет:
Легче тестировать
Когда каждое вычисляемое свойство содержит только очень простое выражение, с очень небольшим набором зависимостей, то будет гораздо проще писать тесты, подтверждающие его правильную работу.
Легче читать
Упрощение вычисляемых свойств заставляет вас давать каждому значению понятное имя, даже если оно не будет использоваться повторно. Это облегчает другим разработчикам (и вам в будущем) сосредоточиться на коде, который им нужен и выяснить что происходит.
Лучше приспособлены к изменяющимся требованиям
Любое значение, которое можно назвать, может быть полезным для представления. Например, мы можем решить отображать сообщение пользователю с информацией сколько денег сэкономил. Мы также можем решить рассчитывать налог с продаж, но, возможно, отображать его отдельно, а не как часть окончательной цены.
Небольшие, сфокусированные вычисляемые свойства создают меньше предположений о том, как информация будет использована, поэтому при изменениях требований потребуется меньше рефакторинга.
Плохо
computed: {
price: function () {
var basePrice = this.manufactureCost / (1 - this.profitMargin)
return (
basePrice -
basePrice * (this.discountPercent || 0)
)
}
}
Хорошо
computed: {
basePrice: function () {
return this.manufactureCost / (1 - this.profitMargin)
},
discount: function () {
return this.basePrice * (this.discountPercent || 0)
},
finalPrice: function () {
return this.basePrice - this.discount
}
}
Значения атрибутов в кавычках настоятельно рекомендуется
Непустые значения HTML-атрибутов должны быть обрамлены кавычками (одинарными или двойными, в зависимости от того, что не используется в JS).
Хотя значения атрибутов без каких-либо пробелов не требуют иметь кавычки в HTML, эта практика зачастую приводит к избеганию использования пробелов, делая значения атрибутов менее читабельными.
Сокращённая запись директив настоятельно рекомендуется
Сокращённую запись директив (:
для v-bind:
, @
для v-on:
и #
для v-slot
) следует использовать всегда или никогда.
Плохо
<input
v-bind:value="newTodoText"
:placeholder="newTodoInstructions"
>
<input
v-on:input="onInput"
@focus="onFocus"
>
<template v-slot:header>
<h1>Здесь может быть заголовок страницы</h1>
</template>
<template #footer>
<p>Здесь контактная информация</p>
</template>
Хорошо
<input
:value="newTodoText"
:placeholder="newTodoInstructions"
>
<input
v-bind:value="newTodoText"
v-bind:placeholder="newTodoInstructions"
>
<input
@input="onInput"
@focus="onFocus"
>
<input
v-on:input="onInput"
v-on:focus="onFocus"
>
<template v-slot:header>
<h1>Здесь может быть заголовок страницы</h1>
</template>
<template v-slot:footer>
<p>Здесь контактная информация</p>
</template>
<template #header>
<h1>Здесь может быть заголовок страницы</h1>
</template>
<template #footer>
<p>Здесь контактная информация</p>
</template>
Правила приоритета C: Рекомендуется (Минимизация произвольных выборов и накладных расходов)
Порядок опций компонента/экземпляра рекомендуется
Опции компонента/экземпляра должны быть упорядочены консистентно.
Это порядок по умолчанию, который мы рекомендуем для опций компонентов. Они разделены на категории, поэтому вы поймёте, где добавлять новые свойства из плагинов.
Побочные эффекты (вызывает эффекты вне компонента)
el
Глобальная осведомлённость (требует знаний вне компонента)
name
parent
Тип компонента (изменяет тип компонента)
functional
Модификаторы шаблона (изменение способа компиляции шаблонов)
delimiters
comments
Зависимости шаблона (ресурсы, используемые в шаблоне)
components
directives
filters
Композиция (объединение свойств в опциях)
extends
mixins
Интерфейс (интерфейс компонента)
inheritAttrs
model
props
/propsData
Локальное состояние (локальные реактивные свойства)
data
computed
События (коллбэки вызываемые реактивными событиями)
watch
- События хуков жизненного цикла (в порядке их вызова)
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
activated
deactivated
beforeDestroy
destroyed
Нереактивные свойства (свойства экземпляра независимые от системы реактивности)
methods
Отрисовка (декларативное описание вывода компонента)
template
/render
renderError
Порядок атрибутов элемента рекомендуется
Атрибуты элементов (в том числе компонентов) должны быть упорядочены консистентно.
Этот порядок по умолчанию мы рекомендуем для опций компонентов. Они разделены на категории, поэтому вы узнаете, где добавлять пользовательские атрибуты и директивы.
Определение (предоставляет параметры компонента)
is
Отображение списка (создаёт несколько вариантов одного элемента)
v-for
Условия (указывается отрисовывается/отображается ли элемент)
v-if
v-else-if
v-else
v-show
v-cloak
Модификаторы отрисовки (изменяют способ отрисовки элемента)
v-pre
v-once
Глобальная осведомлённость (требует знаний вне компонента)
id
Уникальные атрибуты (атрибуты, требующие уникальных значений)
ref
key
Двусторонняя привязка (объединение привязки и событий)
v-model
Другие атрибуты (все неуказанные связанные или несвязанные атрибуты)
События (обработчики событий компонента)
v-on
Содержимое (перезаписывает содержимое элемента)
v-html
v-text
Пустые строки между опций компонента/экземпляра рекомендуется
Вы можете добавить одну пустую строку между многострочными свойствами, особенно если опции не могут больше помещаться на вашем экране без прокрутки.
Когда компоненты кажутся неразборчивыми и становятся трудными для чтения, то добавление пустых строк между многострочными свойствами может облегчить их беглое изучение просматривая взглядом. В некоторых редакторах, таких как Vim, параметры форматирования подобные этому также могут облегчить навигацию с клавиатуры.
Хорошо
props: {
value: {
type: String,
required: true
},
focused: {
type: Boolean,
default: false
},
label: String,
icon: String
},
computed: {
formattedValue: function () {
// ...
},
inputClasses: function () {
// ...
}
}
// Отсутствие пробелов не мешает, если компонент
// всё ещё легко читать и перемещаться по нему.
props: {
value: {
type: String,
required: true
},
focused: {
type: Boolean,
default: false
},
label: String,
icon: String
},
computed: {
formattedValue: function () {
// ...
},
inputClasses: function () {
// ...
}
}
Порядок секций в однофайловых компонентах рекомендуется
Однофайловые компоненты должны всегда использовать один порядок для корневых тегов секций <script>
, <template>
и <style>
, заканчиваясь <style>
, потому что всегда требуется хотя бы одна из двух других.
Плохо
<style>/* ... */</style>
<script>/* ... */</script>
<template>...</template>
<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>
<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
Хорошо
<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>
<!-- ComponentB.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>
<!-- ComponentA.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
Правила приоритета D: Используйте с осторожностью (Потенциально опасные паттерны)
v-if
/v-else-if
/v-else
без key
используйте с осторожностью
Обычно лучше использовать key
вместе с v-if
+ v-else
, если они являются одним и тем же типом элемента (например, когда оба элемента <div>
).
По умолчанию Vue обновляет DOM максимально эффективно. Это означает, что при переключении между элементами одного и того же типа, он просто исправляет текущий элемент, а не заменяет его на новый. Это может привести к непреднамеренным побочным эффектам, если эти элементы не должны считаться одинаковыми.
Хорошо
<div
v-if="error"
key="search-status"
>
Ошибка: {{ error }}
</div>
<div
v-else
key="search-results"
>
{{ results }}
</div>
Селекторы элементов при использовании scoped
используйте с осторожностью
Селекторов элементов следует избегать при использовании scoped
.
Воспользуйтесь селекторами классов вместо селекторов элементов в стилях с атрибутом scoped
, потому что большое количество селекторов элементов отрабатывает медленно.
Подробное объяснение
Для ограничения области действия стилей Vue добавляет уникальный атрибут к элементам компонента, например, такой как data-v-f3f3eg9
. Затем селекторы изменяются так, чтобы воздействовали только на подходящие элементы с этим атрибутом (например, button[data-v-f3f3eg9]
).
Проблема в том, что большое количество селекторов атрибутов элементов (например, button[data-v-f3f3eg9]
) будет значительно медленнее селекторов классов (например, .btn-close[data-v-f3f3eg9]
), поэтому селекторы классов должны быть предпочтительными всегда, когда это возможно.
Плохо
<template>
<button>X</button>
</template>
<style scoped>
button {
background-color: red;
}
</style>
Хорошо
<template>
<button class="btn btn-close">X</button>
</template>
<style scoped>
.btn-close {
background-color: red;
}
</style>
Неявная коммуникация между родительским и дочерними компонентами используйте с осторожностью
Входные параметры и события должны быть предпочтительным способом коммуникации между родительским и дочерними компонентами, вместо использования this.$parent
или изменения входных параметров.
В идеальном Vue приложении входные параметры передаются вниз, события всплывают наверх. Придерживаясь этого соглашения ваши компоненты будет намного легче понять. Тем не менее, есть крайние случаи, когда изменения входных параметров или использование this.$parent
могут упростить два компонента, которые уже глубоко связаны между собой.
Проблема в том, что есть также множество простых случаев, когда эти шаблоны могут показаться удобнее. Остерегайтесь: не соблазняйтесь кажущейся простоте (чтобы понять поток вашего состояния) для краткосрочной выгоды (написания чуть меньшего количества кода).
Плохо
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: '<input v-model="todo.text">'
})
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
methods: {
removeTodo() {
var vm = this
vm.$parent.todos = vm.$parent.todos.filter(function(todo) {
return todo.id !== vm.todo.id
})
}
},
template: `
<span>
{{ todo.text }}
<button @click="removeTodo">
X
</button>
</span>
`
})
Хорошо
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: `
<input
:value="todo.text"
@input="$emit('input', $event.target.value)"
>
`
})
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: `
<span>
{{ todo.text }}
<button @click="$emit('delete')">
X
</button>
</span>
`
})
Управление состоянием приложения без flux используйте с осторожностью
Vuex должен быть предпочтительным способом для глобального управления состоянием приложения вместо использования this.$root
или глобальной шины событий.
Управление состоянием через this.$root
и/или использование глобальной шины событий может быть удобным для очень простых случаев, но не подходит для большинства приложений.
Vuex — официальная flux-подобная реализация для Vue, и предлагает не только централизованное место для управления состоянием, а также инструменты организации, отслеживания и отладки изменений состояния. Она хорошо интегрируется в экосистему Vue (включая полную поддержку Vue DevTools).
Плохо
// main.js
new Vue({
data: {
todos: []
},
created: function() {
this.$on('remove-todo', this.removeTodo)
},
methods: {
removeTodo: function(todo) {
var todoIdToRemove = todo.id
this.todos = this.todos.filter(function(todo) {
return todo.id !== todoIdToRemove
})
}
}
})
Хорошо
// store/modules/todos.js
export default {
state: {
list: []
},
mutations: {
REMOVE_TODO(state, todoId) {
state.list = state.list.filter(todo => todo.id !== todoId)
}
},
actions: {
removeTodo({ commit, state }, todo) {
commit('REMOVE_TODO', todo.id)
}
}
}
<!-- TodoItem.vue -->
<template>
<span>
{{ todo.text }}
<button @click="removeTodo(todo)">
X
</button>
</span>
</template>
<script>
import { mapActions } from 'vuex'
export default {
props: {
todo: {
type: Object,
required: true
}
},
methods: mapActions(['removeTodo'])
}
</script>