Vue.js 코딩가이드 : Essential(Error Prevention)

Vue.js 코딩가이드 : Essential(Error Prevention)

늘 Vue를 사용하지만 코딩 스타일 가이드를 정독한 적은 없는 관계로.. 조금 시간이 난 김에 한번 스타일가이드를 정리해보려 합니다.

참고한 문서는 Vue.js Official Style Guide.

모든 가이드가 그렇듯, 절대적인 지표는 아니며 개발 방향을 정해주는 정도로 참고하면 되겠습니다.

Rule Categories

이하의 룰들은 총 4가지 카테고리로 나뉘게 된다.

명칭 우선순위 설명
Priority A Essential - 에러를 방지한다.
- 룰의 예외는 극히 드물다.
Priority B Strongly Recommended - 코드의 가독성/개발 편의성을 높여준다.
- 해당 룰을 지키지 않아도 코드는 실행될 수 있지만 가급적 룰을 따르는 것이 좋다.
Priority C Recommended - 허용가능한/타당한 이유의 다른 방식이 있을때에는 꼭 따르지 않아도 된다.
- 해당 룰에서는 가능한 다른 방법과 함께 default option을 제안한다. 개발자는 이들 중 자신의 프로젝트에 맞춰 따를 룰을 정할 수 있다.
- 이러한 Community rule을 따르면 다른 코드를 이식해올 때 용이하다
Priority D Use with caution - 희귀한 edge case들을 처리하거나 legacy code의 migration을 위한 룰
- 남용시 코드 가독성을 떨어뜨리고 오류를 발생시킬 수 있다.

Priority A Rules : Essential(Error Prevention)

Multi-word component names

  • 컴포넌트명은 반드시 여러개의 단어로 되어있어야 한다.(Root의 App 컴포넌트나 , 같은 내장 컴포넌트 제외)

  • 현재 존재하는, 앞으로 만들어질 수 있는 HTML 태그와의 혼선을 방지할 수 있다.(모든 HTML 태그는 single word)

Bad

1
2
3
Vue.component('todo', {
// ...
})

Good

1
2
3
Vue.component('todo-item', {
// ...
})

Component Data

  • 컴포넌트의 data반드시 함수형이어야 한다.(object 반환, new Vue 초기화시 제외)
  • 컴포넌트 data를 단순한 object형으로 선언할 경우 컴포넌트의 재사용성에 큰 문제 생긴다.
    • 해당 컴포넌트를 사용하는 모든 instance에서 같은 data object를 참조한다.
    • 즉, 컴포넌트의 데이터가 its own data가 아닌 shared data가 되어버린다. 어떤 하나가 행하는 deletion/adding/changing은 모든 다른 컴포넌트들에 영향을 미침.
  • 따라서 각 컴포넌트는 자신만의 고유한 데이터를 가질 수 있어야하며, 컴포넌트의 data 필드를 특정 데이터 객체를 반환하는 함수형으로 선언함으로서 문제를 해결할 수 있다.

Bad

1
2
3
4
5
Vue.component('some-comp', {
data: {
foo: 'bar'
}
})

Good

1
2
3
4
5
6
7
Vue.component('some-comp', {
data: function () {
return {
foo: 'bar'
}
}
})

Exception

1
2
3
4
5
6
7
8
// 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'
}
})

Prop definition

  • prop은 가능한 한 상세하게 필드를 정의하는 것이 좋다.
  • 최소한 type은 명세해야 한다.

Bad

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

Good

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Not bad
props: {
status: String
}

// Even better!
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
'syncing',
'synced',
'version-conflict',
'error'
].indexOf(value) !== -1
}
}
}

Keyed v-for

  • v-for 사용시에는 항상 key가 있어야 한다.
  • v-for의 key는 내부 subtree의 제어에 사용된다.
  • object consistancy를 유지하는 등의 예측가능한 행위를 제어/관리할 수 있도록 두는것이 좋다.
    • 예를들어 리스트 아이템 [b,a]를 알파벳 순서로 정렬한다고 가정해보자.
    • Vue는 가장 비용이 적게 드는 방식을 선택할 것. 가장 간단한 방법은 b를 삭제한 뒤 a 뒤에 다시 넣는 것이다.
    • 하지만 만약 애니메이션 등을 사용해서 b를 삭제하면 안된다면? b가 텍스트필드라 정렬후에도 b에대한 focus를 잃어선 안된다면?
    • 이때 b, a 각각에 고유한 키를 부여한다면 Vue가 보다 더 predictable 하게 행동하도록 제어할 수 있다.

Bad

1
2
3
4
5
<ul>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>

Good

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

Avoid v-if with v-for

  • v-for과 v-if를 같은 element 내에서 사용하면 안된다.

    • vue가 directive를 처리할떄 v-forv-if보다 높은 우선순위를 갖는다.
    • 따라서 v-for와 v-if를 같이 사용한 아래와 같은 코드는
    1
    2
    3
    4
    5
    <ul>
    <li v-for="user in users" v-if="user.isActive" :key="user.id">
    {{ user.name }}
    </li>
    </ul>

    다음과 같이 변환된다.

    1
    2
    3
    4
    5
    this.users.map(function (user) {
    if (user.isActive) {
    return user.name
    }
    })

    따라서 user의 정보가 일부분만 바뀌어도 re-render를 위해 리스트 전체를 다시 iterate해야한다.

    • 위와같은 경우

      1. if조건을 v-for 컴포넌트의 상위 컴포넌트로 옮긴다

      2. Computed 메서드에 user에 대한 필터링 조건을 설정하여 v-for로 iterate되는 리스트 자체를 필터링된 리스트로 사용한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <ul>
    <li
    v-for="user in activeUsers"
    :key="user.id"
    >
    {{ user.name }}
    </li>
    </ul>

    ...

    computed: {
    activeUsers: function () {
    return this.users.filter(function (user) {
    return user.isActive
    })
    }
    }

Component style scoping

  • Root의 App컴포넌트를 제외한 모든 컴포넌트의 스타일은 scoped 되어야 한다 :