[優(yōu)先級(jí) A:必要的]
這些規(guī)則會(huì)幫你規(guī)避錯(cuò)誤,所以學(xué)習(xí)并接受它們帶來(lái)的全部代價(jià)吧械哟。
[優(yōu)先級(jí) B:強(qiáng)烈推薦]
這些規(guī)則能夠在絕大多數(shù)工程中改善可讀性和開(kāi)發(fā)體驗(yàn)朵栖。即使你違反了,代碼還是能照常運(yùn)行糟港,但例外應(yīng)該盡可能少且有合理的理由琉兜。
[優(yōu)先級(jí) C:推薦]
當(dāng)存在多個(gè)同樣好的選項(xiàng)凯正,選任意一個(gè)都可以確保一致性。在這些規(guī)則里豌蟋,我們描述了每個(gè)選項(xiàng)并建議一個(gè)默認(rèn)的選擇廊散。也就是說(shuō)只要保持一致且理由充分,你可以隨意在你的代碼庫(kù)中做出不同的選擇梧疲。
[優(yōu)先級(jí) D:謹(jǐn)慎使用]
有些 Vue 特性的存在是為了照顧極端情況或幫助老代碼的平穩(wěn)遷移允睹。當(dāng)被過(guò)度使用時(shí),這些特性會(huì)讓你的代碼難于維護(hù)甚至變成 bug 的來(lái)源幌氮。這些規(guī)則是為了給有潛在風(fēng)險(xiǎn)的特性敲個(gè)警鐘缭受,并說(shuō)明它們什么時(shí)候不應(yīng)該使用以及為什么。
├── src 項(xiàng)目源碼目錄
│ ├── main.js 入口js文件
│ ├── App.vue 根組件
│ ├── assets 靜態(tài)資源目錄该互,公共的靜態(tài)資源米者,圖片,字體等
│ │ ├── fonts 字體資源
│ │ ├── images 圖片資源
│ ├── mixin 全局混入文件夾
│ ├── directives 全局指令文件夾
│ ├── mock mock數(shù)據(jù)文件夾
│ │ ├── index.js mock入口文件
│ │ ├── module 模塊文件夾
│ │ │ │── user.js 模塊mock數(shù)據(jù)
│ ├── service api文件夾
│ │ │ └── service.ubas.js api文件
│ ├── components 組件目錄
│ │ ├── common 公共組文件夾
│ │ │ │── upload 上傳組件文件夾
│ │ ├── home 模塊組件文件夾
│ │ │ └── HomeBanner.vue 主頁(yè)banner組件
│ ├── utils 封裝的工具文件夾
│ │ ├── axios.interceptors.js Axios攔截器封裝
│ │ ├── constants.js 接口環(huán)境常量
│ │ ├── views.utils.js 樣式邏輯公共方法
│ │ ├── vue.common.js vue全局公共方法
│ │ ├── vue.http.js axios 的封裝
│ ├── store 數(shù)據(jù)中心
│ │ ├── state.js state 數(shù)據(jù)源,數(shù)據(jù)定義
│ │ ├── mutations.js 同步修改 state 數(shù)據(jù)的方法定義
│ │ ├── mutation-types.js 定義 Mutations 的類(lèi)型
│ │ ├── actions.js 異步修改 state 數(shù)據(jù)的方法定義
│ │ ├── getters.js 獲取數(shù)據(jù)的方法定義
│ │ └── index.js 數(shù)據(jù)中心入口文件
│ ├── routes 前端路由
│ │ └── index.js
│ └── views 頁(yè)面目錄蔓搞,所有業(yè)務(wù)邏輯的頁(yè)面都應(yīng)該放這里
│ │ ├── home
│ │ │ ├── HomeIndex.vue
│ │ │ ├── children
│ │ │ │ ├── HomeText.vue
一. 代碼注釋
代碼是由人為編寫(xiě)并維護(hù)的胰丁。請(qǐng)確保你的代碼能夠自描述、注釋良好并且易于他人理解喂分。好的代碼注釋能夠傳達(dá)上下文關(guān)系和代碼目的锦庸。不要簡(jiǎn)單地重申組件或 class 名稱(chēng)。
bad
good
0妻顶、組件名不為index.vue
原則上view文件夾是用來(lái)存放路由的頁(yè)面的,所以應(yīng)該只存放路由指向的vue組件,或者子路由指向的組件酸员,
并且路由組件不建議直接使用 index.vue
來(lái)命名蜒车,編輯器切換匹配文件名不友好讳嘱。
bad
userInfo/
|- index.vue
good
userInfo/
|- userInfoIndex.vue
1、組件名為多個(gè)單詞
組件名應(yīng)該始終是多個(gè)單詞的酿愧,根組件 App 除外沥潭。
這樣做可以避免跟現(xiàn)有的以及未來(lái)的 HTML 元素相沖突,因?yàn)樗械?HTML 元素名稱(chēng)都是單個(gè)單詞的嬉挡。
bad
Vue.component('todo', {
// ...
})
export default {
name: 'Todo',
// ...
}
good
Vue.component('todoItem', {
// ...
})
export default {
name: 'TodoItem',
// ...
}
2钝鸽、組件數(shù)據(jù)
組件的 data 必須是一個(gè)函數(shù)。
當(dāng)在組件中使用 data 屬性的時(shí)候 (除了 new Vue 外的任何地方)庞钢,它的值必須是返回一個(gè)對(duì)象的函數(shù)拔恰。
(如果data是一個(gè)函數(shù)的話(huà),這樣每復(fù)用一次組件基括,就會(huì)返回一份新的data颜懊,類(lèi)似于給每個(gè)組件實(shí)例創(chuàng)建一個(gè)私有的數(shù)據(jù)空間,讓各個(gè)組件實(shí)例維護(hù)各自的數(shù)據(jù)风皿。而單純的寫(xiě)成對(duì)象形式河爹,就使得所有組件實(shí)例共用了一份data,就會(huì)造成一個(gè)變了全都會(huì)變的結(jié)果)
bad
Vue.component('someComp', {
data: {
foo: 'bar'
}
})
export default {
data: {
foo: 'bar'
}
}
good
Vue.component('someComp', {
data: function () {
return {
foo: 'bar'
}
}
})
export default {
data () {
return {
foo: 'bar'
}
}
}
3桐款、Prop 定義
Prop 定義應(yīng)該盡量詳細(xì)咸这。
在你提交的代碼中,prop 的定義應(yīng)該盡量詳細(xì)魔眨,至少需要指定其類(lèi)型媳维。
細(xì)致的prop 定義有兩個(gè)好處:
(1)它們寫(xiě)明了組件的 API,所以很容易看懂組件的用法遏暴;
(2)在開(kāi)發(fā)環(huán)境下侨艾,如果向一個(gè)組件提供格式不正確的 prop,Vue 將會(huì)告警拓挥,以幫助你捕獲潛在的錯(cuò)誤來(lái)源唠梨。
bad
props: ['status']
good
props: {
status: {
type: String,
required: true,
validator: function (value) {
}
}
}
4、為 v-for
設(shè)置鍵值
在組件上總是必須用 key
配合 v-for
侥啤,以便維護(hù)內(nèi)部組件及其子樹(shù)的狀態(tài)当叭。
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
用在一起
永遠(yuǎn)不要把 v-if
和 v-for
同時(shí)用在同一個(gè)元素上。
一般我們?cè)趦煞N常見(jiàn)的情況下會(huì)傾向于這樣做:
- 為了過(guò)濾一個(gè)列表中的項(xiàng)目 (比如
v-for="user in users" v-if="user.isActive"
)蚁鳖。在這種情形下磺芭,請(qǐng)將users
替換為一個(gè)計(jì)算屬性 (比如activeUsers
),讓其返回過(guò)濾后的列表醉箕。- 為了避免渲染本應(yīng)該被隱藏的列表 (比如
v-for="user in users" v-if="shouldShowUsers"
)钾腺。這種情形下,請(qǐng)將v-if
移動(dòng)至容器元素上 (比如ul讥裤、ol
)放棒。
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、為組件樣式設(shè)置作用域
對(duì)于應(yīng)用來(lái)說(shuō)己英,頂級(jí)App
組件和布局組件中的樣式可以是全局的间螟,但是其它所有組件都應(yīng)該是有作用域的。
對(duì)于組件庫(kù)损肛,我們應(yīng)該更傾向于選用基于 class
的策略而不是 scoped
特性厢破。
使用唯一的class
名可以幫你確保那些三方庫(kù)的CSS
不會(huì)運(yùn)用在你自己的HTML
上。
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>
<!-- 使用 `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>
<!-- 使用 BEM 約定 -->
<style>
.c-Button {
border: none;
border-radius: 2px;
}
.c-Button--close {
background-color: red;
}
</style>
7治拿、私有屬性名
在插件摩泪、混入等擴(kuò)展中始終為自定義的私有屬性使用 $_
前綴厚棵。并附帶一個(gè)命名空間以回避和其它作者的沖突 (比如 $_yourPluginName_
)抵拘。
bad
var myGreatMixin = {
// ...
methods: {
update: function () {
// ...
}
}
}
good
var myGreatMixin = {
// ...
methods: {
$_myGreatMixin_update: function () {
// ...
}
}
}
8、單文件組件文件的大小寫(xiě)
單文件組件的文件名應(yīng)該要么始終是小駝峰扰才。
bad
components/
|- MyComponent.vue
good
components/
|- myComponent.vue
9同波、基礎(chǔ)組件名鳄梅。
應(yīng)用特定樣式和約定的基礎(chǔ)組件 (也就是展示類(lèi)的、無(wú)邏輯的或無(wú)狀態(tài)的組件) 應(yīng)該全部以一個(gè)特定的前綴開(kāi)頭未檩,比如 Base
戴尸、App
。
bad
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
good
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
10冤狡、緊密耦合的組件名
和父組件緊密耦合的子組件應(yīng)該以父組件名作為前綴命名孙蒙。
如果一個(gè)組件只在某個(gè)父組件的場(chǎng)景下有意義,這層關(guān)系應(yīng)該體現(xiàn)在其名字上悲雳。因?yàn)榫庉嬈魍ǔ?huì)按字母順序組織文件挎峦,所以這樣做可以把相關(guān)聯(lián)的文件排在一起。
bad
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
good
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
11合瓢、組件名中的單詞順序
組件名應(yīng)該以高級(jí)別的 (通常是一般化描述的) 單詞開(kāi)頭坦胶,以描述性的修飾詞結(jié)尾。
bad
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
good
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue
12、自閉合組件
在單文件組件顿苇、字符串模板和JSX中沒(méi)有內(nèi)容的組件應(yīng)該是自閉合的峭咒,但在 DOM 模板里永遠(yuǎn)不要這樣做。
HTML 并不支持自閉合的自定義元素
bad
<!-- 在 DOM 模板中 -->
<my-component/>
good
<!-- 在 DOM 模板中 -->
<my-component></my-component>
13纪岁、模板中的組件名大小寫(xiě)
由于 HTML 是大小寫(xiě)不敏感的凑队,在 DOM 模板中必須仍使用 kebab-case。
bad
<!-- 在 DOM 模板中 -->
<MyComponent></MyComponent>
good
<!-- 在所有地方 -->
<my-component></my-component>
14幔翰、JS/JSX 中的組件名大小寫(xiě)
JS/[JSX中的組件名應(yīng)該始終是 camelCase的漩氨,盡管在較為簡(jiǎn)單的應(yīng)用中只使用 Vue.component
進(jìn)行全局組件注冊(cè)時(shí),可以使用 kebab-case 字符串遗增。
bad
import MyComponent from './MyComponent.vue'
export default {
name: 'MyComponent',
// ...
}
good
import myComponent from './myComponent.vue'
export default {
name: 'myComponent',
// ...
}
15叫惊、完整單詞的組件名
組組件名應(yīng)該傾向于完整單詞而不是縮寫(xiě)。
bad
components/
|- SdSettings.vue
|- UProfOpts.vue
good
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
16贡定、Prop 名大小寫(xiě)
組組件名應(yīng)該傾向于完整單詞而不是縮寫(xiě)赋访。
bad
props: {
'greeting-text': String
}
<WelcomeMessage greetingText="hi"/>
good
props: {
greetingText: String
}
<WelcomeMessage greeting-text="hi"/>
17可都、模板中簡(jiǎn)單的表達(dá)式
組件模板應(yīng)該只包含簡(jiǎn)單的表達(dá)式缓待,復(fù)雜的表達(dá)式則應(yīng)該重構(gòu)為計(jì)算屬性或方法。
bad
{{
fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
good
<!-- 在模板中 -->
{{ normalizedFullName }}
// 復(fù)雜表達(dá)式已經(jīng)移入一個(gè)計(jì)算屬性
computed: {
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}
18渠牲、帶引號(hào)的 attribute
值
非空 HTML attribute 值應(yīng)該始終帶雙引號(hào) 旋炒。
bad
<input type=text>
<AppSidebar :style={width:sidebarWidth+'px'}>
good
<input type="text">
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
19、指令縮寫(xiě)
指令縮寫(xiě) (用 : 表示 v-bind:签杈、用 @ 表示 v-on: ) 瘫镇。
bad
<input
v-bind:value="newTodoText"
:placeholder="newTodoInstructions"
>
<input
v-on:input="onInput"
@focus="onFocus"
>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
good
<input
:value="newTodoText"
:placeholder="newTodoInstructions"
>
<input
@input="onInput"
@focus="onFocus"
>
20、簡(jiǎn)單的計(jì)算屬性
應(yīng)該把復(fù)雜計(jì)算屬性分割為盡可能多的更簡(jiǎn)單的 property
答姥。
bad
computed: {
price: function () {
var basePrice = this.manufactureCost / (1 - this.profitMargin)
return (
basePrice -
basePrice * (this.discountPercent || 0)
)
}
}
good
computed: {
basePrice: function () {
return this.manufactureCost / (1 - this.profitMargin)
},
discount: function () {
return this.basePrice * (this.discountPercent || 0)
},
finalPrice: function () {
return this.basePrice - this.discount
}
}
21铣除、多個(gè) attribute 的元素
多個(gè) attribute 的元素應(yīng)該分多行撰寫(xiě),每個(gè) attribute 一行鹦付。
bad
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent foo="a" bar="b" baz="c"/>
good
<img
src="https://vuejs.org/images/logo.png"
alt="Vue Logo"
>
<MyComponent
foo="a"
bar="b"
baz="c"
/>
22尚粘、單例組件名
只應(yīng)該擁有單個(gè)活躍實(shí)例的組件應(yīng)該以 The
前綴命名,以示其唯一性敲长。
這不意味著組件只可用于一個(gè)單頁(yè)面郎嫁,而是每個(gè)頁(yè)面只使用一次。這些組件永遠(yuǎn)不接受任何 prop祈噪,因?yàn)樗鼈兪菫槟愕膽?yīng)用定制的泽铛,而不是它們?cè)谀愕膽?yīng)用中的上下文。如果你發(fā)現(xiàn)有必要添加 prop辑鲤,那就表明這實(shí)際上是一個(gè)可復(fù)用的組件盔腔,只是目前在每個(gè)頁(yè)面里只使用一次。
bad
components/
|- Heading.vue
|- MySidebar.vue
good
components/
|- TheHeading.vue
|- TheSidebar.vue
23、scoped
中的元素選擇器
在 scoped 樣式中弛随,類(lèi)選擇器比元素選擇器更好澈蝙,因?yàn)榇罅渴褂迷剡x擇器是很慢的。
為了給樣式設(shè)置作用域撵幽,Vue 會(huì)為元素添加一個(gè)獨(dú)一無(wú)二的
attribute
灯荧,例如data-v-f3f3eg9
。然后修改選擇器盐杂,使得在匹配選擇器的元素中逗载,只有帶這個(gè)attribute
才會(huì)真正生效 (比如button[data-v-f3f3eg9]
)。
問(wèn)題在于大量的元素和attribute
組合的選擇器 (比如button[data-v-f3f3eg9]
) 會(huì)比類(lèi)和attribute
組合的選擇器慢链烈,所以應(yīng)該盡可能選用類(lèi)選擇器厉斟。
bad
<template>
<button>X</button>
</template>
<style scoped>
button {
background-color: red;
}
</style>
good
<template>
<button class="btn btn-close">X</button>
</template>
<style scoped>
.btn-close {
background-color: red;
}
</style>
24、隱性的父子組件通信
應(yīng)該優(yōu)先通過(guò)prop
和事件進(jìn)行父子組件之間的通信强衡,而不是 this.$parent
或變更prop
擦秽。
一個(gè)理想的 Vue 應(yīng)用是
prop
向下傳遞,事件向上傳遞的漩勤。遵循這一約定會(huì)讓你的組件更易于理解感挥。然而,在一些邊界情況下prop
的變更或this.$parent
能夠簡(jiǎn)化兩個(gè)深度耦合的組件越败。
問(wèn)題在于触幼,這種做法在很多簡(jiǎn)單的場(chǎng)景下可能會(huì)更方便。但請(qǐng)當(dāng)心究飞,不要為了一時(shí)方便 (少寫(xiě)代碼) 而犧牲數(shù)據(jù)流向的簡(jiǎn)潔性 (易于理解)置谦。
bad
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>
`
})
good
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>
`
})
25、沒(méi)有在 v-if/v-else-if/v-else
中使用 key
如果一組 v-if
+ v-else
的元素類(lèi)型相同亿傅,最好使用 key
(比如兩個(gè) <div>
元素)媒峡。
默認(rèn)情況下,Vue 會(huì)盡可能高效的更新 DOM葵擎。這意味著其在相同類(lèi)型的元素之間切換時(shí)谅阿,會(huì)修補(bǔ)已存在的元素,而不是將舊的元素移除然后在同一位置添加一個(gè)新元素坪蚁。如果本不相同的元素被識(shí)別為相同奔穿,則會(huì)出現(xiàn)意料之外的結(jié)果。
bad
<div v-if="error">
錯(cuò)誤:{{ error }}
</div>
<div v-else>
{{ results }}
</div>
good
<div
v-if="error"
key="search-status"
>
錯(cuò)誤:{{ error }}
</div>
<div
v-else
key="search-results"
>
{{ results }}
</div>
26敏晤、全局狀態(tài)管理
應(yīng)該優(yōu)先通過(guò) Vuex
管理全局狀態(tài)贱田,而不是通過(guò) this.$root
或一個(gè)全局事件總線。
通過(guò) this.$root
在很多簡(jiǎn)單的情況下都是很方便的嘴脾,但是并不適用于絕大多數(shù)的應(yīng)用男摧。
Vuex 是 Vue 的官方類(lèi) flux 實(shí)現(xiàn)蔬墩,其提供的不僅是一個(gè)管理狀態(tài)的中心區(qū)域,還是組織耗拓、追蹤和調(diào)試狀態(tài)變更的好工具拇颅。
bad
// 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
})
}
}
})
good
// 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>
26、postMessage格式
無(wú)論是業(yè)務(wù)之間或業(yè)務(wù)和框架通信乔询,postmessage內(nèi)容都應(yīng)遵循固定格式
good
postMessage(
{
from:"",//發(fā)出業(yè)務(wù)名稱(chēng)
to:"",//接受業(yè)務(wù)名稱(chēng)
data:"",//傳遞數(shù)據(jù)
}
)