前言這里是官方的 Vue 特有代碼的風(fēng)格指南。如果在工程中使用 Vue,為了回避錯(cuò)誤、小糾結(jié)和反模式,該指南是份不錯(cuò)的參考。規(guī)則歸類優(yōu)先級(jí) A:必要這些規(guī)則會(huì)幫你規(guī)避錯(cuò)誤,所以學(xué)習(xí)并接受它們帶來的全部代價(jià)吧。這里面可能存在例外,但應(yīng)該非常少,且只有你同時(shí)精通 JavaScript 和 Vue 才可以這樣做。優(yōu)先級(jí) B:推薦這些規(guī)則能夠在絕大多數(shù)工程中改善可讀性和開發(fā)體驗(yàn)。即使你違反了,代碼還是能照常運(yùn)行,但例外應(yīng)該盡可能少且有合理的理由。優(yōu)先級(jí) C:謹(jǐn)慎使用有些 Vue 特性的存在是為了照顧極端情況或幫助老代碼的平穩(wěn)遷移。當(dāng)被過度使用時(shí),這些特性會(huì)讓你的代碼難于維護(hù)甚至變成 bug 的來源。這些規(guī)則是為了給有潛在風(fēng)險(xiǎn)的特性敲個(gè)警鐘,并說明它們什么時(shí)候不應(yīng)該使用以及為什么。優(yōu)先級(jí) A 的規(guī)則:必要的 (規(guī)避錯(cuò)誤)組件名為多個(gè)單詞 必要組件名應(yīng)該始終是多個(gè)單詞的,根組件 App 以及 <transition> 、 <component> 之類的 Vue 內(nèi)置組件除外。這樣做可以避免跟現(xiàn)有的以及未來的 HTML 元素相沖突,因?yàn)樗械?HTML 元素名稱都是單個(gè)單詞的。反例
好例子Vue.component('todo-item', {
組件數(shù)據(jù) 必要組件的 data 必須是一個(gè)函數(shù)。當(dāng)在組件中使用 data 屬性的時(shí)候 (除了 newVue 外的任何地方),它的值必須是返回一個(gè)對(duì)象的函數(shù)。詳解當(dāng) data 的值是一個(gè)對(duì)象時(shí),它會(huì)在這個(gè)組件的所有實(shí)例之間共享。想象一下,假如一個(gè) TodoList 組件的數(shù)據(jù)是這樣的:我們可能希望重用這個(gè)組件,允許用戶維護(hù)多個(gè)列表 (比如分為購物、心愿單、日常事務(wù)等)。這時(shí)就會(huì)產(chǎn)生問題。因?yàn)槊總€(gè)組件的實(shí)例都引用了相同的數(shù)據(jù)對(duì)象,更改其中一個(gè)列表的標(biāo)題就會(huì)改變其它每一個(gè)列表的標(biāo)題。增刪改一個(gè)待辦事項(xiàng)的時(shí)候也是如此。取而代之的是,我們希望每個(gè)組件實(shí)例都管理其自己的數(shù)據(jù)。為了做到這一點(diǎn),每個(gè)實(shí)例必須生成一個(gè)獨(dú)立的數(shù)據(jù)對(duì)象。在 JavaScript 中,在一個(gè)函數(shù)中返回這個(gè)對(duì)象就可以了:
反例Vue.component('some-comp', {
好例子Vue.component('some-comp', {
// 在一個(gè) Vue 的根實(shí)例上直接使用對(duì)象是可以的, // 因?yàn)橹淮嬖谝粋€(gè)這樣的實(shí)例。
Prop 定義 必要在你提交的代碼中,prop 的定義應(yīng)該盡量詳細(xì),至少需要指定其類型。詳解細(xì)致的 prop 定義有兩個(gè)好處:- 它們寫明了組件的 API,所以很容易看懂組件的用法;
- 在開發(fā)環(huán)境下,如果向一個(gè)組件提供格式不正確的 prop,Vue 將會(huì)告警,以幫助你捕獲潛在的錯(cuò)誤來源。
反例// 這樣做只有開發(fā)原型系統(tǒng)時(shí)可以接受
好例子 validator: function (value) {
避免 v-if 和 v-for 用在一起 必要一般我們?cè)趦煞N常見的情況下會(huì)傾向于這樣做:- 為了過濾一個(gè)列表中的項(xiàng)目 (比如
v-for='user in users'v-if='user.isActive' )。在這種情形下,請(qǐng)將 users 替換為一個(gè)計(jì)算屬性 (比如 activeUsers ),讓其返回過濾后的列表。 - 為了避免渲染本應(yīng)該被隱藏的列表 (比如
v-for='user in users'v-if='shouldShowUsers' )。這種情形下,請(qǐng)將 v-if 移動(dòng)至容器元素上 (比如 ul , ol )。
詳解當(dāng) Vue 處理指令時(shí), v-for 比 v-if 具有更高的優(yōu)先級(jí),所以這個(gè)模板:將會(huì)經(jīng)過如下運(yùn)算:this.users.map(function (user) {
因此哪怕我們只渲染出一小部分用戶的元素,也得在每次重渲染的時(shí)候遍歷整個(gè)列表,不論活躍用戶是否發(fā)生了變化。通過將其更換為在如下的一個(gè)計(jì)算屬性上遍歷: activeUsers: function () { return this.users.filter(function (user) {
v-for='user in activeUsers'
- 過濾后的列表只會(huì)在
users 數(shù)組發(fā)生相關(guān)變化時(shí)才被重新運(yùn)算,過濾更高效。 - 使用
v-for='user in activeUsers' 之后,我們?cè)阡秩镜臅r(shí)候只遍歷活躍用戶,渲染更高效。 - 解藕渲染層的邏輯,可維護(hù)性 (對(duì)邏輯的更改和擴(kuò)展) 更強(qiáng)。
<ul v-if='shouldShowUsers'>
通過將 v-if 移動(dòng)到容器元素,我們不會(huì)再對(duì)列表中的每個(gè)用戶檢查 shouldShowUsers 。取而代之的是,我們只檢查它一次,且不會(huì)在 shouldShowUsers 為否的時(shí)候運(yùn)算 v-for 。反例
好例子 v-for='user in activeUsers'
<ul v-if='shouldShowUsers'>
優(yōu)先級(jí) B 的規(guī)則:推薦 (增強(qiáng)可讀性)組件文件 推薦只要有能夠拼接文件的構(gòu)建系統(tǒng),就把每個(gè)組件單獨(dú)分成文件。當(dāng)你需要編輯一個(gè)組件或查閱一個(gè)組件的用法時(shí),可以更快速的找到它。反例Vue.component('TodoList', {
Vue.component('TodoItem', {
好例子
組件名中的單詞順序 推薦組件名應(yīng)該以高級(jí)別的 (通常是一般化描述的) 單詞開頭,以描述性的修飾詞結(jié)尾。要注意在你的應(yīng)用中所謂的“高級(jí)別”是跟語境有關(guān)的。比如對(duì)于一個(gè)帶搜索表單的應(yīng)用來說,它可能包含這樣的組件:|- ExcludeFromSearchInput.vue |- LaunchOnStartupCheckbox.vue
你可能注意到了,我們很難看出來哪些組件是針對(duì)搜索的?,F(xiàn)在我們來根據(jù)規(guī)則給組件重新命名:|- SearchInputExcludeGlob.vue |- SettingsCheckboxLaunchOnStartup.vue |- SettingsCheckboxTerms.vue
因?yàn)榫庉嬈魍ǔ?huì)按字母順序組織文件,所以現(xiàn)在組件之間的重要關(guān)系一目了然。你可能想換成多級(jí)目錄的方式,把所有的搜索組件放到“search”目錄,把所有的設(shè)置組件放到“settings”目錄。我們只推薦在非常大型 (如有 100+ 個(gè)組件) 的應(yīng)用下才考慮這么做,因?yàn)椋?/section>- 在多級(jí)目錄間找來找去,要比在單個(gè)
components 目錄下滾動(dòng)查找要花費(fèi)更多的精力。 - 存在組件重名 (比如存在多個(gè)
ButtonDelete 組件) 的時(shí)候在編輯器里更難快速定位。 - 讓重構(gòu)變得更難,因?yàn)闉橐粋€(gè)移動(dòng)了的組件更新相關(guān)引用時(shí),查找/替換通常并不高效。
反例|- ExcludeFromSearchInput.vue |- LaunchOnStartupCheckbox.vue
好例子|- SearchInputExcludeGlob.vue |- SettingsCheckboxTerms.vue |- SettingsCheckboxLaunchOnStartup.vue
完整單詞的組件名 推薦編輯器中的自動(dòng)補(bǔ)全已經(jīng)讓書寫長命名的代價(jià)非常之低了,而其帶來的明確性卻是非常寶貴的。不常用的縮寫尤其應(yīng)該避免。反例
好例子|- StudentDashboardSettings.vue |- UserProfileOptions.vue
Prop 名大小寫 推薦在聲明 prop 的時(shí)候,其命名應(yīng)該始終使用 camelCase,而在模板和 JSX 中應(yīng)該始終使用 kebab-case。我們單純的遵循每個(gè)語言的約定。在 JavaScript 中更自然的是 camelCase。而在 HTML 中則是 kebab-case。反例
好例子多個(gè)特性的元素 推薦多個(gè)特性的元素應(yīng)該分多行撰寫,每個(gè)特性一行。在 JavaScript 中,用多行分隔對(duì)象的多個(gè)屬性是很常見的最佳實(shí)踐,因?yàn)檫@樣更易讀。模板和 JSX 值得我們做相同的考慮。反例<img src='https:///images/logo.png' alt='Vue Logo'>
<MyComponent foo='a' bar='b' baz='c'/>
好例子 src='https:///images/logo.png'
簡單的計(jì)算屬性 推薦應(yīng)該把復(fù)雜計(jì)算屬性分割為盡可能多的更簡單的屬性。詳解更簡單、命名得當(dāng)?shù)挠?jì)算屬性是這樣的:- 當(dāng)每個(gè)計(jì)算屬性都包含一個(gè)非常簡單且很少依賴的表達(dá)式時(shí),撰寫測(cè)試以確保其正確工作就會(huì)更加容易。
- 簡化計(jì)算屬性要求你為每一個(gè)值都起一個(gè)描述性的名稱,即便它不可復(fù)用。這使得其他開發(fā)者 (以及未來的你) 更容易專注在他們關(guān)心的代碼上并搞清楚發(fā)生了什么。
- 任何能夠命名的值都可能用在視圖上。舉個(gè)例子,我們可能打算展示一個(gè)信息,告訴用戶他們存了多少錢;也可能打算計(jì)算稅費(fèi),但是可能會(huì)分開展現(xiàn),而不是作為總價(jià)的一部分。小的、專注的計(jì)算屬性減少了信息使用時(shí)的假設(shè)性限制,所以需求變更時(shí)也用不著那么多重構(gòu)了。
反例 var basePrice = this.manufactureCost / (1 - this.profitMargin) basePrice * (this.discountPercent || 0)
好例子 return this.manufactureCost / (1 - this.profitMargin) return this.basePrice * (this.discountPercent || 0) finalPrice: function () { return this.basePrice - this.discount
優(yōu)先級(jí) C 的規(guī)則:謹(jǐn)慎使用 (有潛在危險(xiǎn)的模式)沒有在 v-if / v-else-if / v-else 中使用 key 謹(jǐn)慎使用如果一組 v-if + v-else 的元素類型相同,最好使用 key (比如兩個(gè) <div> 元素)。默認(rèn)情況下,Vue 會(huì)盡可能高效的更新 DOM。這意味著其在相同類型的元素之間切換時(shí),會(huì)修補(bǔ)已存在的元素,而不是將舊的元素移除然后在同一位置添加一個(gè)新元素。如果本不相同的元素被識(shí)別為相同,則會(huì)出現(xiàn)意料之外的結(jié)果。反例
好例子scoped 中的元素選擇器 謹(jǐn)慎使用元素選擇器應(yīng)該避免在 scoped 中出現(xiàn)。在 scoped 樣式中,類選擇器比元素選擇器更好,因?yàn)榇罅渴褂迷剡x擇器是很慢的。詳解為了給樣式設(shè)置作用域,Vue 會(huì)為元素添加一個(gè)獨(dú)一無二的特性,例如 data-v-f3f3eg9 。然后修改選擇器,使得在匹配選擇器的元素中,只有帶這個(gè)特性才會(huì)真正生效 (比如 button[data-v-f3f3eg9] )。問題在于大量的元素和特性組合的選擇器 (比如 button[data-v-f3f3eg9] ) 會(huì)比類和特性組合的選擇器慢,所以應(yīng)該盡可能選用類選擇器。反例
好例子 <button class='btn btn-close'>X</button>
隱性的父子組件通信 謹(jǐn)慎使用應(yīng)該優(yōu)先通過 prop 和事件進(jìn)行父子組件之間的通信,而不是 this.$parent 或改變 prop。一個(gè)理想的 Vue 應(yīng)用是 prop 向下傳遞,事件向上傳遞的。遵循這一約定會(huì)讓你的組件更易于理解。然而,在一些邊界情況下 prop 的變更或 this.$parent 能夠簡化兩個(gè)深度耦合的組件。問題在于,這種做法在很多簡單的場景下可能會(huì)更方便。但請(qǐng)當(dāng)心,不要為了一時(shí)方便 (少寫代碼) 而犧牲數(shù)據(jù)流向的簡潔性 (易于理解)。反例Vue.component('TodoItem', { template: '<input v-model='todo.text'>'
Vue.component('TodoItem', { vm.$parent.todos = vm.$parent.todos.filter(function (todo) { return todo.id !== vm.todo.id <button @click='removeTodo'>
好例子Vue.component('TodoItem', { @input='$emit('input', $event.target.value)'
Vue.component('TodoItem', { <button @click='$emit('delete')'>
非 Flux 的全局狀態(tài)管理 謹(jǐn)慎使用應(yīng)該優(yōu)先通過 Vuex 管理全局狀態(tài),而不是通過 this.$root 或一個(gè)全局事件總線。通過 this.$root 和/或全局事件總線管理狀態(tài)在很多簡單的情況下都是很方便的,但是并不適用于絕大多數(shù)的應(yīng)用。Vuex 提供的不僅是一個(gè)管理狀態(tài)的中心區(qū)域,還是組織、追蹤和調(diào)試狀態(tài)變更的好工具。反例 this.$on('remove-todo', this.removeTodo) removeTodo: function (todo) { var todoIdToRemove = todo.id this.todos = this.todos.filter(function (todo) { return todo.id !== todoIdToRemove
好例子// store/modules/todos.js REMOVE_TODO (state, todoId) { state.list = state.list.filter(todo => todo.id !== todoId) removeTodo ({ commit, state }, todo) { commit('REMOVE_TODO', todo.id)
<button @click='removeTodo(todo)'>
import { mapActions } from 'vuex'
methods: mapActions(['removeTodo'])
https://github.com/vuejs/cn./blob/master/src/v2/style-guide/index.md
|