Skip to content

현재 이 문서는 번역 작업이 진행중입니다

Priority A Rules: Essential

우선순쉬 A 등급 규칙: 필수

These rules help prevent errors, so learn and abide by them at all costs. Exceptions may exist, but should be very rare and only be made by those with expert knowledge of both JavaScript and Vue.

Use multi-word component names

여러 단어로 된 컴포넌트명을 사용하세요

User component names should always be multi-word, except for root App components. This prevents conflicts with existing and future HTML elements, since all HTML elements are a single word.

사용자 컴포넌트의 이름은 항상 여러 단어되어 있어야 합니다. (단 루트 컴포넌트인 App은 제외) 이 규칙을 지키게 되면 HTML의 표준에서는 모든 앨리먼트가 단일 단어로 정의 되기 때문에 HTML 엘리먼트와 충돌이 방지됩니다.

나쁨

<!-- in pre-compiled templates -->
<Item />

<!-- in in-DOM templates -->
<item></item>

좋음

<!-- in pre-compiled templates -->
<TodoItem />

<!-- in in-DOM templates -->
<todo-item></todo-item>

Use detailed prop definitions

prop 정의를 상세하게 하세요.

In committed code, prop definitions should always be as detailed as possible, specifying at least type(s).

코드를 커밋할때, prop을 가능한한 디테일하게 정의하세요. 최소한 타입이라도 기술하세요.

Detailed Explanation

Detailed prop definitions have two advantages:

  • They document the API of the component, so that it's easy to see how the component is meant to be used.
  • In development, Vue will warn you if a component is ever provided incorrectly formatted props, helping you catch potential sources of error.

Bad

// This is only OK when prototyping
props: ['status']

Good

props: {
  status: String
}
// Even better!
props: {
  status: {
    type: String,
    required: true,

    validator: value => {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].includes(value)
    }
  }
}

Use keyed v-for

key with v-for is always required on components, in order to maintain internal component state down the subtree. Even for elements though, it's a good practice to maintain predictable behavior, such as object constancy in animations.

Detailed Explanation

Let's say you have a list of todos:

data() {
  return {
    todos: [
      {
        id: 1,
        text: 'Learn to use v-for'
      },
      {
        id: 2,
        text: 'Learn to use key'
      }
    ]
  }
}

Then you sort them alphabetically. When updating the DOM, Vue will optimize rendering to perform the cheapest DOM mutations possible. That might mean deleting the first todo element, then adding it again at the end of the list.

The problem is, there are cases where it's important not to delete elements that will remain in the DOM. For example, you may want to use <transition-group> to animate list sorting, or maintain focus if the rendered element is an <input>. In these cases, adding a unique key for each item (e.g. :key="todo.id") will tell Vue how to behave more predictably.

In our experience, it's better to always add a unique key, so that you and your team simply never have to worry about these edge cases. Then in the rare, performance-critical scenarios where object constancy isn't necessary, you can make a conscious exception.

Bad

<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>

Good

<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

Avoid v-if with v-for

Never use v-if on the same element as v-for.

There are two common cases where this can be tempting:

  • To filter items in a list (e.g. v-for="user in users" v-if="user.isActive"). In these cases, replace users with a new computed property that returns your filtered list (e.g. activeUsers).

  • To avoid rendering a list if it should be hidden (e.g. v-for="user in users" v-if="shouldShowUsers"). In these cases, move the v-if to a container element (e.g. ul, ol).

Detailed Explanation

When Vue processes directives, v-if has a higher priority than v-for, so that this template:

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

Will throw an error, because the v-if directive will be evaluated first and the iteration variable user does not exist at this moment.

This could be fixed by iterating over a computed property instead, like this:

computed: {
  activeUsers() {
    return this.users.filter(user => user.isActive)
  }
}
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

Alternatively, we can use a <template> tag with v-for to wrap the <li> element:

<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

Bad

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

Good

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

Use component-scoped styling

For applications, styles in a top-level App component and in layout components may be global, but all other components should always be scoped.

This is only relevant for Single-File Components. It does not require that the scoped attribute be used. Scoping could be through CSS modules, a class-based strategy such as BEM, or another library/convention.

Component libraries, however, should prefer a class-based strategy instead of using the scoped attribute.

This makes overriding internal styles easier, with human-readable class names that don't have too high specificity, but are still very unlikely to result in a conflict.

Detailed Explanation

If you are developing a large project, working with other developers, or sometimes include 3rd-party HTML/CSS (e.g. from Auth0), consistent scoping will ensure that your styles only apply to the components they are meant for.

Beyond the scoped attribute, using unique class names can help ensure that 3rd-party CSS does not apply to your own HTML. For example, many projects use the button, btn, or icon class names, so even if not using a strategy such as BEM, adding an app-specific and/or component-specific prefix (e.g. ButtonClose-icon) can provide some protection.

Bad

<template>
  <button class="btn btn-close">×</button>
</template>

<style>
.btn-close {
  background-color: red;
}
</style>

Good

<template>
  <button class="button button-close">×</button>
</template>

<!-- Using the `scoped` attribute -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

.button-close {
  background-color: red;
}
</style>
<template>
  <button :class="[$style.button, $style.buttonClose]">×</button>
</template>

<!-- Using CSS modules -->
<style module>
.button {
  border: none;
  border-radius: 2px;
}

.buttonClose {
  background-color: red;
}
</style>
<template>
  <button class="c-Button c-Button--close">×</button>
</template>

<!-- Using the BEM convention -->
<style>
.c-Button {
  border: none;
  border-radius: 2px;
}

.c-Button--close {
  background-color: red;
}
</style>

Avoid exposing private functions in mixins

Always use the $_ prefix for custom private properties in a plugin, mixin, etc that should not be considered public API. Then to avoid conflicts with code by other authors, also include a named scope (e.g. $_yourPluginName_).

Detailed Explanation

Vue uses the _ prefix to define its own private properties, so using the same prefix (e.g. _update) risks overwriting an instance property. Even if you check and Vue is not currently using a particular property name, there is no guarantee a conflict won't arise in a later version.

As for the $ prefix, its purpose within the Vue ecosystem is special instance properties that are exposed to the user, so using it for private properties would not be appropriate.

Instead, we recommend combining the two prefixes into $_, as a convention for user-defined private properties that guarantee no conflicts with Vue.

Bad

const myGreatMixin = {
  // ...
  methods: {
    update() {
      // ...
    }
  }
}
const myGreatMixin = {
  // ...
  methods: {
    _update() {
      // ...
    }
  }
}
const myGreatMixin = {
  // ...
  methods: {
    $update() {
      // ...
    }
  }
}
const myGreatMixin = {
  // ...
  methods: {
    $_update() {
      // ...
    }
  }
}

Good

const myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update() {
      // ...
    }
  }
}
// Even better!
const myGreatMixin = {
  // ...
  methods: {
    publicMethod() {
      // ...
      myPrivateFunction()
    }
  }
}

function myPrivateFunction() {
  // ...
}

export default myGreatMixin
Priority A Rules: Essential has loaded