Vue 네이밍 가이드 - WastepaperBasket/Vue.js GitHub Wiki

1. 컴포넌트 이름에 합성어 사용

root 컴포넌트인 App 컴포넌트를 제외하고는 컴포넌트의 이름은 항상 "합성-어"를 사용해야 한다.

모든 HTML 엘리먼트의 이름은 한 단어로 표현되기 때문에, 충돌을 방지하려면 컴포넌트를 잘 설명할 수 있는 하이픈으로 구분된 합성어를 사용하는것이 좋다.

  • Bad
Vue.component('todo', {
  // ...
})
export default {
  name: 'Todo',
  // ...
}
  • Good
Vue.component('todo-item', {
  // ...
})
export default {
  name: 'TodoItem',
  // ...
}

2. 컴포넌트 데이터

컴포넌트 데이터는 함수여야 한다.

한 컴포넌트에서 data 프로퍼티를 사용할 때, 그 값은 무조건 한 객체를 리턴하는 함수여야 한다.

함수가 아닌 그냥 일반 객체일 경우, 컴포넌트를 두 영역에 렌더링 시 같은 data 객체를 참조하게 되어,

두 컴포넌트가 같은 값을 공유하게 되는 문제가 발생한다.

(의도적인 거라면 할말없지만, mvvm 패턴을 따르지 않는 잘못된 방식이다.)

따라서 각 사용처마다 독립적인 data 객체를 가져야 하므로 꼭 함수로 정의하여 리터럴 객체를 리턴하도록 구현해야 한다.

  • Bad
Vue.component('some-comp', {
  data: {
    foo: 'bar'
  }
})
export default {
  data: {
    foo: 'bar'
  }
}
  • Good
Vue.component('some-comp', {
  data: function () {
    return {
      foo: 'bar'
    }
  }
})
// In a .vue file
export default {
  data () {
    return {
      foo: 'bar'
    }
  }
}
// It's OK to use an object directly in a root
// Vue instance, since only a single instance
// will ever exist.
new Vue({
  data: {
    foo: 'bar'
  }
})

3. Props 정의

Prop 정의는 가능한 가장 자세히 기술해야 한다.

최소 type 이라도 기술하자.

  • Bad
// This is only OK when prototyping
props: ['status']
  • Good
props: {
  status: String
}
// Even better!
props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].indexOf(value) !== -1
    }
  }
}

4. v-for에 key 지정

v-for 사용시 항상 key를 지정해라.

내부 컴포넌트의 하위 트리의 상태를 유지하기 위해 key 가 필요하다.

Vue.js 는 가상 돔 렌더링을 한다.

v-for 와 같은 디렉티브는 바인딩 된 배열에 따라 내용이 그려진다.

배열에 아이템이 하나라도 추가되면 전체 내용을 다시 그려야 하는 비싼 연산이 될 수 있다.

이련 경우를 방지하고자, 각 아이템의 key를 알려주면 정확한 비교가 가능하므로,

가상돔 업데이트 연산 과정이 명확하고 빠르게 수행되어 불필요한 업데이트를 방지 할 수 있다.

  • 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>

5. v-if 와 v-for 를 동시에 사용하지 마세요

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
  }
})

따라서 users 의 일부분만 렌더링 하려는 의도임에도,

active user 가 변경되었는지 여부에 관계없이,

리렌더링 할때마다 전체 users 목록을 항상 순회해야 한다.

그 대신 아래와 같이 계산된 프로퍼티를 순회해라.

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 배열에 active 변경사항이 있는 경우만 다시 계산되므로 필터링이 훨씬 효과적이다.

v-for="users in activeUsers" 를 사용하면,

렌더링 동안에 active users 만 순회하므로 렌더링이 훨씬 효과적이다.

로직과 화면 영역이 분리되어, 유지관리가 훨씬 수월 해 진다.

  • Bad
<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>
  • Good
<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>

6. 컴포넌트 스타일 스코프

최상단 App 컴포넌트와 layout 관련 컴포넌트의 스타일은 전역이 될 수도 있지만,

다른 모든 컴포넌트의 스타일은 항상 scoped 여야 한다.

이거는 단순히 싱글 파일 컴포넌트에 대한 얘기이다.

항상 scoped 속성을 요구하는것이 아니다.

스코핑은 CSS module 을 통해 정의될 수도있고,

BEM 과 같은 클래스-기반 방법도 있다.

컴포넌트 라이브러리들은 scoped 속성을 사용하는 대신 클래스-기반 전략을 취해야 한다.

클래스-기반 전략을 취하면 내부 스타일을 재정의 하는것이 더 쉬우며,

사람이 읽을 수 있는 클래스 이름을 사용하면(높은 특정도를 가지지는 않지만) 충돌을 일으키지는 않는다.

만약 다른 개발자들과, 외부 다른 서드파티 HTML/CSS 를 포함한 큰 프로젝트를 개발한다고 한다면,

일관된 스코핑은 스타일이 의도된 컴포넌트에만 적용되도록 확신할 수 있다.

scpoed 속성 외에도, 유니크한 클래스이름 사용은 서드파티와 충돌을 일으키지 않는데 도움을 받을 수 있다.

예를들면 많은 프로젝트가 button, btn, icon 같은 클래스 이름을 사용하는데,

굳이 BEM 스타일을 사용하지 않아도, 프로젝트나 및 컴포넌트 별로 ButtonClose-icon 같은 접두사를 추가하면

충돌을 방지할 수 있는 보호장치를 마련할 수 있다.

  • Bad
<template>
  <button class="btn btn-close">X</button>
</template>

<style>
.btn-close {
  background-color: red;
}
</style>
  • Good
<template>
  <button class="button button-close">X</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]">X</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">X</button>
</template>

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

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

7. Private 속성 이름

plugin, mixin 등 을 정의할 때 private 속성은 항상 $_ 접두어를 사용하여 정의해라.

접두어는 Vue 에서 private 속성들을 정의하는데 사용하므로,

접두어 사용은 vue 인스턴스의 속성을 오버라이딩 할 수 있는 위험성이 있다.

만약 그 속성명이 사용되지 않음을 확인했다 치더라도, 다음 버전에서 충돌이 일어나지 않을것이라는 보장이 없다.

$ 접두어는 Vue 에코시스템 안에서 사용자에게 노출시키려는 의미를 가진 특별한 속성이다. 따라서 $ 접두어 사용도 적절치 않다.

대신 두 접두어를 혼합한 $_ 접두어를 추천한다. 컨벤션으로 $_ 접두어는 사용자 정의 private 속성을 보장하여 Vue 와 충돌이 없다.

  • Bad
var myGreatMixin = {
  // ...
  methods: {
    update: function () {
      // ...
    }
  }
}
var myGreatMixin = {
  // ...
  methods: {
    _update: function () {
      // ...
    }
  }
}
var myGreatMixin = {
  // ...
  methods: {
    $update: function () {
      // ...
    }
  }
}
var myGreatMixin = {
  // ...
  methods: {
    $_update: function () {
      // ...
    }
  }
}
  • Good
var myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update: function () {
      // ...
    }
  }
}
⚠️ **GitHub.com Fallback** ⚠️