Vue 네이밍 가이드 - WastepaperBasket/Vue.js GitHub Wiki
root 컴포넌트인 App 컴포넌트를 제외하고는 컴포넌트의 이름은 항상 "합성-어"를 사용해야 한다.
모든 HTML 엘리먼트의 이름은 한 단어로 표현되기 때문에, 충돌을 방지하려면 컴포넌트를 잘 설명할 수 있는 하이픈으로 구분된 합성어를 사용하는것이 좋다.
- Bad
Vue.component('todo', {
// ...
})
export default {
name: 'Todo',
// ...
}
- Good
Vue.component('todo-item', {
// ...
})
export default {
name: 'TodoItem',
// ...
}
컴포넌트 데이터는 함수여야 한다.
한 컴포넌트에서 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'
}
})
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
}
}
}
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>
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>
최상단 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>
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 () {
// ...
}
}
}