Это официальное руководство по стилю для Vue-специфичного кода. Если вы используете Vue в проекте, это отличная возможность избежать ошибок и антишаблонов. Однако мы не считаем, что любое подобное руководство идеально подходит для всех команд или проектов, поэтому разумные отклонения поощряются на основе полученного опыта, окружающего технологического набора и личных ценностей.
В большинстве случаев мы также будем избегать общих рекомендаций о JavaScript или HTML. Мы не против, используете ли вы точки с запятой или висячие запятые. Мы не против, используются ли в вашем HTML одинарные или двойные кавычки для значений атрибутов. Однако некоторые исключения будут присутствовать для тех случаев, где мы нашли конкретный шаблон полезным в контексте Vue.
Скоро мы предоставим рекомендации по соблюдению единого стиля кода. Иногда вам нужно просто быть дисциплинированным, но там, где это возможно, мы постараемся показать, как можно использовать ESLint и другие автоматизированные процессы, чтобы упростить соблюдение единого стиля.
Наконец, мы разделили правила на четыре категории:
Эти правила помогут предотвратить ошибки, поэтому изучите и соблюдайте их любой ценой. Исключения могут быть, но должны быть очень редкими и должны выполняться только теми, у кого есть хорошие знания как JavaScript, так и Vue.
Эти правила помогут улучшить читаемость и/или опыт разработки в большинстве проектов. Ваш код всё равно будет работать, если вы нарушите их, но нарушения должны быть редкими и обоснованными.
Где существует множество одинаково хороших вариантов, можно сделать собственный выбор для обеспечения согласованности. В этих правилах мы описываем каждый приемлемый вариант и предлагаем выбор по умолчанию. Это означает, что вы можете свободно делать другой выбор в вашей собственной кодовой базе, если вы придерживаетесь согласованности и имеете вескую причину. Пожалуйста, не стесняйтесь! Приспосабливаясь к стандартам сообщества, вы будете:
Некоторые возможности Vue существуют для приспособления к редким крайним случаям или для обеспечения более плавному переезду старой кодовой базы. Однако при чрезмерном использовании они сделают ваш код более сложным в поддержке или могут стать источником ошибок. Эти правила освещают потенциально опасные функции, объясняя, когда и почему их следует избегать.
Имена компонентов должны всегда состоять из нескольких слов, за исключением корневого компонента App
и встроенных компонентов самого Vue, например, <transition>
или <component>
.
Это предотвращает конфликты с существующими или будущими HTML-элементами, поскольку все HTML-элементы именуются одним словом.
Vue.component('todo', {
// ...
})
export default {
name: 'Todo'
// ...
}
Vue.component('todo-item', {
// ...
})
export default {
name: 'TodoItem'
// ...
}
Свойство data
компонента должно быть функцией.
При использовании свойства data
в компоненте (т.е. везде, за исключением new Vue
), значением всегда должна быть функция, возвращающая объект.
Когда значением data
будет объект, он будет использован для всех экземпляров компонента. Представьте, например, компонент TodoList
с таким набором данных:
data: {
listTitle: '',
todos: []
}
Мы можем захотеть повторно использовать этот компонент, позволяя пользователям использовать несколько списков (например, для покупок, списков желаний, ежедневных дел и т.п.). Однако есть проблема. Поскольку каждый экземпляр компонента ссылается на один и тот же объект данных, изменение названия одного списка также изменит заголовок всех остальных списков. То же самое случится и при добавлении/изменении/удалении элемента списка.
Вместо этого мы хотим, чтобы каждый экземпляр компонента управлял только своими собственными данными. Чтобы этого добиться, каждый экземпляр должен сгенерировать собственный уникальный объект данных. В JavaScript этого возможно достигнуть, возвращая объект из функции:
data: function () {
return {
listTitle: '',
todos: []
}
}
Vue.component('some-comp', {
data: {
foo: 'bar'
}
})
export default {
data: {
foo: 'bar'
}
}
Vue.component('some-comp', {
data: function() {
return {
foo: 'bar'
}
}
})
// В .vue-файле
export default {
data() {
return {
foo: 'bar'
}
}
}
// Допускается использовать объект напрямую
// в корневом экземпляре Vue, поскольку только
// один экземпляр будет существовать.
new Vue({
data: {
foo: 'bar'
}
})
Входные параметры должны быть определены как можно более подробно.
В готовом коде определение входных параметров всегда должно быть максимально подробным, по крайней мере определяя тип данных.
Подробное определение входных параметров имеет два преимущества:
// Этого достаточно лишь для прототипа
props: ['status']
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, как вести себя более предсказуемо.
По нашему опыту, лучше всегда добавлять уникальный ключ, чтобы вам и вашей команде никогда не приходилось беспокоиться об этих крайних случаях. Затем в редких критически зависимых от производительности случаях, когда согласованность объекта не требуется — вы можете сделать сознательное исключение.
<ul>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
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
Всякий раз, когда система сборки позволяет конкатенировать файлы, каждый компонент должен быть в собственном файле.
Это поможет вам быстрее найти компонент, когда потребуется его отредактировать или просмотреть как его использовать.
Vue.component('TodoList', {
// ...
})
Vue.component('TodoItem', {
// ...
})
components/
|- TodoList.js
|- TodoItem.js
components/
|- TodoList.vue
|- TodoItem.vue
Имена файлов однофайловых компонентов должны быть всегда в PascalCase или всегда в kebab-case.
PascalCase лучше всего работает с автодополнением в редакторах кода, поскольку он согласуется с тем, как мы ссылаемся на компоненты в JS(X) и шаблонах. Тем не менее, смешанные имена файлов иногда могут создавать проблемы для нечувствительных к регистру файловых систем, поэтому kebab-case также вполне приемлем.
components/
|- mycomponent.vue
components/
|- myComponent.vue
components/
|- MyComponent.vue
components/
|- my-component.vue
Базовые компоненты (известные как презентационные, глупые или чистые компоненты) которые применяют специфичные для вашего приложения стили или соглашения должны начинаться с определённого префикса, такого как Base
, App
или V
.
Эти компоненты закладывают основу для согласованности стилей и поведения в вашем приложении. Они могут содержать только:
Но они никогда не содержат глобальное состояние (например, из хранилища 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/
|- 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
Это не рекомендуется, так как это приводит к:
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
Компоненты должны именоваться с высшего уровня (часто наиболее общих слов) и заканчиваться описательными дополняющими словами.
Вам может быть интересно:
«Почему мы заставляем называть компоненты менее естественным языком?»
На естественном английском прилагательные и другие описатели обычно располагаются перед существительными, в то время как исключения требуют слов-соединителей. Например:
Вы определённо можете включать эти слова-соединители в именах компонентах если хотите, но порядок всё ещё важен.
Также обратите внимание, то что считается «высоким уровнем» будет относиться к вашему приложению. Например, представьте приложение с формой для поиска. Оно может содержать компоненты наподобие таких:
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:
<MyComponent>
более зрительно отличается от элемента HTML из одного слова, нежели <my-component>
, потому что есть две заметных разницы в символах (две заглавных), а не только одна (тире).К сожалению, из-за нечувствительности 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 всегда должен быть PascalCase, хотя они могут быть в kebab-case внутри строк для простых приложений, которые используют только глобальную регистрацию компонентов через Vue.component
.
В JavaScript PascalCase — это соглашение для классов и конструкторов прототипов — по существу всё, что может иметь разные экземпляры. Компоненты Vue также могут иметь экземпляры, поэтому также имеет смысл использовать PascalCase. В качестве дополнительного преимущества, использование PascalCase в JSX (и шаблонах) позволяет изучающим код легче различать компоненты от HTML-элементов.
Однако, для приложений, которые используют только глобальные определения компонентов через Vue.component
, мы рекомендуем вместо него использовать 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'
// ...
}
Имена компонентов должны состоять из полных слов, а не аббревиатур.
Автодополнение в редакторах уменьшают сложность написания более длинных имён, а ясность, которую они предоставляют неоценима. Малоизвестных аббревиатур, в частности, следует избегать.
components/
|- SdSettings.vue
|- UProfOpts.vue
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
Входные параметры должны всегда использовать camelCase при определении, но kebab-case в шаблонах и JSX.
Мы просто придерживаемся соглашений каждого языка. Для JavaScript использовать camelCase является более естественным. Для HTML — kebab-case.
props: {
'greeting-text': String
}
props: {
greetingText: String
}
Элементы с несколькими атрибутами должны располагаться на нескольких строках, по одному атрибуту на строку.
В 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, эта практика зачастую приводит к избеганию использования пробелов, делая значения атрибутов менее читабельными.
<input type=text>
<AppSidebar :style={width:sidebarWidth+'px'}>
<input type="text">
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
Сокращённую запись директив (:
для 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>
Параметры компонента/экземпляра должны быть упорядочены согласованно.
Это порядок по умолчанию, который мы рекомендуем для настроек компонентов. Они разделены на категории, поэтому вы поймёте, где добавлять новые свойства из добавок.
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>
</div>
v-if
/v-else-if
/v-else
без key
используйте с осторожностьюОбычно лучше использовать key
вместе с v-if
+ v-else
, если они являются одним и тем же типом элемента (например, когда оба элемента <div>
).
По умолчанию Vue обновляет dom наиболее эффективно. Это означает, что при переключении между элементами одного и того же типа, он просто исправляет текущий элемент, а не заменяет его на новый. Это может привести к непреднамеренным побочным эффектам, если эти элементы не должны считаться одинаковыми.
<div v-if="error">
Ошибка: {{ error }}
</div>
<div v-else>
{{ results }}
</div>
<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>
`
})
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>