Vue風格指南總結

優先級 A:必要的

這些規則會幫你規避錯誤,因此學習並接受它們帶來的所有代價吧。這裏面可能存在例外,但應該很是少,且只有你同時精通 JavaScript 和 Vue 才能夠這樣作。html

(1)組件名應該始終是多個單詞的,根組件 App 除外vue

組件名爲多個單詞 必要

組件名應該始終是多個單詞的,根組件 App 除外。
這樣作能夠避免跟現有的以及將來的 HTML 元素相沖突,由於全部的 HTML 元素名稱都是單個單詞的。
反例

Vue.component('todo', {
  // ...
})
export default {
  name: 'Todo',
  // ...
}
好例子

Vue.component('todo-item', {
  // ...
})
export default {
  name: 'TodoItem',
  // ...
}
組件名爲多個單詞

(2)組件的 data 必須是一個函數webpack

組件數據 必要

組件的 data 必須是一個函數。
當在組件中使用 data 屬性的時候 (除了 new Vue 外的任何地方),它的值必須是返回一個對象的函數。
 詳解
當 data 的值是一個對象時,它會在這個組件的全部實例之間共享。想象一下,假如一個 TodoList 組件的數據是這樣的:
data: {
  listTitle: '',
  todos: []
}
咱們可能但願重用這個組件,容許用戶維護多個列表 (好比分爲購物、心願單、平常事務等)。這時就會產生問題。由於每一個組件的實例都引用了相同的數據對象,更改其中一個列表的標題就會改變其它每個列表的標題。增刪改一個待辦事項的時候也是如此。
取而代之的是,咱們但願每一個組件實例都管理其本身的數據。爲了作到這一點,每一個實例必須生成一個獨立的數據對象。在 JavaScript 中,在一個函數中返回這個對象就能夠了:
data: function () {
  return {
    listTitle: '',
    todos: []
  }
}
反例

Vue.component('some-comp', {
  data: {
    foo: 'bar'
  }
})
export default {
  data: {
    foo: 'bar'
  }
}
好例子

Vue.component('some-comp', {
  data: function () {
    return {
      foo: 'bar'
    }
  }
})
// In a .vue file
export default {
  data () {
    return {
      foo: 'bar'
    }
  }
}
// 在一個 Vue 的根實例上直接使用對象是能夠的,
// 由於只存在一個這樣的實例。
new Vue({
  data: {
    foo: 'bar'
  }
})
組件數據

(3)Prop 定義應該儘可能詳細web

Prop 定義 必要

Prop 定義應該儘可能詳細。
在你提交的代碼中,prop 的定義應該儘可能詳細,至少須要指定其類型。
 詳解
細緻的 prop 定義有兩個好處:
它們寫明瞭組件的 API,因此很容易看懂組件的用法;
在開發環境下,若是向一個組件提供格式不正確的 prop,Vue 將會告警,以幫助你捕獲潛在的錯誤來源。
反例

// 這樣作只有開發原型系統時能夠接受
props: ['status']
好例子

props: {
  status: String
}
// 更好的作法!
props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].indexOf(value) !== -1
    }
  }
}
Prop 定義

(4)老是用 key 配合 v-forvuex

爲 v-for 設置鍵值 必要

老是用 key 配合 v-for。
在組件上老是必須用 key 配合 v-for,以便維護內部組件及其子樹的狀態。甚至在元素上維護可預測的行爲,好比動畫中的對象固化 (object constancy),也是一種好的作法。
 詳解
假設你有一個待辦事項列表:
data: function () {
  return {
    todos: [
      {
        id: 1,
        text: '學習使用 v-for'
      },
      {
        id: 2,
        text: '學習使用 key'
      }
    ]
  }
}
而後你把它們按照字母順序排序。在更新 DOM 的時候,Vue 將會優化渲染把可能的 DOM 變更降到最低。便可能刪掉第一個待辦事項元素,而後把它從新加回到列表的最末尾。
這裏的問題在於,不要刪除仍然會留在 DOM 中的元素。好比你想使用 <transition-group> 給列表加過渡動畫,或想在被渲染元素是 <input> 時保持聚焦。在這些狀況下,爲每個項目添加一個惟一的鍵值 (好比 :key="todo.id") 將會讓 Vue 知道如何使行爲更容易預測。
根據咱們的經驗,最好始終添加一個惟一的鍵值,以便你和你的團隊永遠沒必要擔憂這些極端狀況。也在少數對性能有嚴格要求的狀況下,爲了不對象固化,你能夠刻意作一些很是規的處理。
反例

<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>
好例子

<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>
爲 v-for 設置鍵值

(5)永遠不要把 v-if 和 v-for 同時用在同一個元素上數組

避免 v-if 和 v-for 用在一塊兒 必要

永遠不要把 v-if 和 v-for 同時用在同一個元素上。
通常咱們在兩種常見的狀況下會傾向於這樣作:
爲了過濾一個列表中的項目 (好比 v-for="user in users" v-if="user.isActive")。在這種情形下,請將 users 替換爲一個計算屬性 (好比 activeUsers),讓其返回過濾後的列表。
爲了不渲染本應該被隱藏的列表 (好比 v-for="user in users" v-if="shouldShowUsers")。這種情形下,請將 v-if 移動至容器元素上 (好比 ul, ol)。
 詳解
當 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
  }
})
所以哪怕咱們只渲染出一小部分用戶的元素,也得在每次重渲染的時候遍歷整個列表,不論活躍用戶是否發生了變化。
經過將其更換爲在以下的一個計算屬性上遍歷:
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 數組發生相關變化時才被從新運算,過濾更高效。
使用 v-for="user in activeUsers" 以後,咱們在渲染的時候只遍歷活躍用戶,渲染更高效。
解藕渲染層的邏輯,可維護性 (對邏輯的更改和擴展) 更強。
爲了得到一樣的好處,咱們也能夠把:
<ul>
  <li
    v-for="user in users"
    v-if="shouldShowUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
更新爲:
<ul v-if="shouldShowUsers">
  <li
    v-for="user in users"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
經過將 v-if 移動到容器元素,咱們不會再對列表中的每一個用戶檢查 shouldShowUsers。取而代之的是,咱們只檢查它一次,且不會在 shouldShowUsers 爲否的時候運算 v-for。
反例

<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>
好例子

<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>
避免 v-if 和 v-for 用在一塊兒

 (6)對於應用來講,頂級 App 組件和佈局組件中的樣式能夠是全局的,可是其它全部組件都應該是有做用域的app

爲組件樣式設置做用域 必要

對於應用來講,頂級 App 組件和佈局組件中的樣式能夠是全局的,可是其它全部組件都應該是有做用域的。
這條規則只和單文件組件有關。你不必定要使用 scoped 特性。設置做用域也能夠經過 CSS Modules,那是一個基於 class 的相似 BEM 的策略,固然你也可使用其它的庫或約定。
無論怎樣,對於組件庫,咱們應該更傾向於選用基於 class 的策略而不是 scoped 特性。
這讓覆寫內部樣式更容易:使用了常人可理解的 class 名稱且沒有過高的選擇器優先級,並且不太會致使衝突。
 詳解
若是你和其餘開發者一塊兒開發一個大型工程,或有時引入三方 HTML/CSS (好比來自 Auth0),設置一致的做用域會確保你的樣式只會運用在它們想要做用的組件上。
不止要使用 scoped 特性,使用惟一的 class 名能夠幫你確保那些三方庫的 CSS 不會運用在你本身的 HTML 上。好比許多工程都使用了 button、btn 或 icon class 名,因此即使你不使用相似 BEM 的策略,添加一個 app 專屬或組件專屬的前綴 (好比 ButtonClose-icon) 也能夠提供不少保護。
反例

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

<style>
.btn-close {
  background-color: red;
}
</style>
好例子

<template>
  <button class="button button-close">X</button>
</template>

<!-- 使用 `scoped` 特性 -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

.button-close {
  background-color: red;
}
</style>
<template>
  <button :class="[$style.button, $style.buttonClose]">X</button>
</template>

<!-- 使用 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>

<!-- 使用 BEM 約定 -->
<style>
.c-Button {
  border: none;
  border-radius: 2px;
}

.c-Button--close {
  background-color: red;
}
</style>
爲組件樣式設置做用域

(7)在插件、混入等擴展中始終爲自定義的私有屬性使用 $_ 前綴。並附帶一個命名空間以迴避和其它做者的衝突 (好比 $_yourPluginName_)。編輯器

私有屬性名 必要

在插件、混入等擴展中始終爲自定義的私有屬性使用 $_ 前綴。並附帶一個命名空間以迴避和其它做者的衝突 (好比 $_yourPluginName_)。
 詳解
Vue 使用 _ 前綴來定義其自身的私有屬性,因此使用相同的前綴 (好比 _update) 有覆寫實例屬性的風險。即使你檢查確認 Vue 當前版本沒有用到這個屬性名,也不能保證和未來的版本沒有衝突。
對於 $ 前綴來講,其在 Vue 生態系統中的目的是暴露給用戶的一個特殊的實例屬性,因此把它用於私有屬性並不合適。
不過,咱們推薦把這兩個前綴結合爲 $_,做爲一個用戶定義的私有屬性的約定,以確保不會和 Vue 自身相沖突。
反例

var myGreatMixin = {
  // ...
  methods: {
    update: function () {
      // ...
    }
  }
}
var myGreatMixin = {
  // ...
  methods: {
    _update: function () {
      // ...
    }
  }
}
var myGreatMixin = {
  // ...
  methods: {
    $update: function () {
      // ...
    }
  }
}
var myGreatMixin = {
  // ...
  methods: {
    $_update: function () {
      // ...
    }
  }
}
好例子

var myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update: function () {
      // ...
    }
  }
}
私有屬性名

 

優先級 B:強烈推薦

這些規則可以在絕大多數工程中改善可讀性和開發體驗。即便你違反了,代碼仍是能照常運行,但例外應該儘量少且有合理的理由。ide

(1)只要有可以拼接文件的構建系統,就把每一個組件單獨分紅文件函數

組件文件 強烈推薦

只要有可以拼接文件的構建系統,就把每一個組件單獨分紅文件。
當你須要編輯一個組件或查閱一個組件的用法時,能夠更快速的找到它。
反例

Vue.component('TodoList', {
  // ...
})

Vue.component('TodoItem', {
  // ...
})
好例子

components/
|- TodoList.js
|- TodoItem.js
components/
|- TodoList.vue
|- TodoItem.vue
組件文件

(2)單文件組件的文件名應該要麼始終是單詞大寫開頭 (PascalCase),要麼始終是橫線鏈接 (kebab-case)。

單文件組件文件的大小寫 強烈推薦

單文件組件的文件名應該要麼始終是單詞大寫開頭 (PascalCase),要麼始終是橫線鏈接 (kebab-case)。
單詞大寫開頭對於代碼編輯器的自動補全最爲友好,由於這使得咱們在 JS(X) 和模板中引用組件的方式儘量的一致。然而,混用文件命名方式有的時候會致使大小寫不敏感的文件系統的問題,這也是橫線鏈接命名一樣徹底可取的緣由。
反例

components/
|- mycomponent.vue
components/
|- myComponent.vue
好例子

components/
|- MyComponent.vue
components/
|- my-component.vue
單文件組件文件的大小寫

(3)應用特定樣式和約定的基礎組件 (也就是展現類的、無邏輯的或無狀態的組件) 應該所有以一個特定的前綴開頭,好比 Base、App 或 V。

基礎組件名 強烈推薦

應用特定樣式和約定的基礎組件 (也就是展現類的、無邏輯的或無狀態的組件) 應該所有以一個特定的前綴開頭,好比 Base、App 或 V。
 詳解
這些組件爲你的應用奠基了一致的基礎樣式和行爲。它們可能只包括:
HTML 元素
其它基礎組件
第三方 UI 組件庫
可是它們毫不會包括全局狀態 (好比來自 Vuex store)。
它們的名字一般包含所包裹元素的名字 (好比 BaseButton、BaseTable),除非沒有現成的對應功能的元素 (好比 BaseIcon)。若是你爲特定的上下文構建相似的組件,那它們幾乎總會消費這些組件 (好比 BaseButton 可能會用在 ButtonSubmit 上)。
這樣作的幾個好處:
當你在編輯器中以字母順序排序時,你的應用的基礎組件會所有列在一塊兒,這樣更容易識別。
由於組件名應該始終是多個單詞,因此這樣作能夠避免你在包裹簡單組件時隨意選擇前綴 (好比 MyButton、VueButton)。
由於這些組件會被頻繁使用,因此你可能想把它們放到全局而不是在各處分別導入它們。使用相同的前綴可讓 webpack 這樣工做:
var requireComponent = require.context("./src", true, /^Base[A-Z]/)
requireComponent.keys().forEach(function (fileName) {
  var baseComponentConfig = requireComponent(fileName)
  baseComponentConfig = baseComponentConfig.default || baseComponentConfig
  var baseComponentName = baseComponentConfig.name || (
    fileName
      .replace(/^.+\//, '')
      .replace(/\.\w+$/, '')
  )
  Vue.component(baseComponentName, baseComponentConfig)
})
反例

components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
好例子

components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
基礎組件名

(4)只應該擁有單個活躍實例的組件應該以 The 前綴命名,以示其惟一性

單例組件名 強烈推薦

只應該擁有單個活躍實例的組件應該以 The 前綴命名,以示其惟一性。
這不意味着組件只可用於一個單頁面,而是每一個頁面只使用一次。這些組件永遠不接受任何 prop,由於它們是爲你的應用定製的,而不是它們在你的應用中的上下文。若是你發現有必要添加 prop,那就代表這其實是一個可複用的組件,只是目前在每一個頁面裏只使用一次。
反例

components/
|- Heading.vue
|- MySidebar.vue
好例子

components/
|- TheHeading.vue
|- TheSidebar.vue
單例組件名

(5)和父組件緊密耦合的子組件應該以父組件名做爲前綴命名

緊密耦合的組件名 強烈推薦

和父組件緊密耦合的子組件應該以父組件名做爲前綴命名。
若是一個組件只在某個父組件的場景下有意義,這層關係應該體如今其名字上。由於編輯器一般會按字母順序組織文件,因此這樣作能夠把相關聯的文件排在一塊兒。
 詳解
你能夠試着經過在其父組件命名的目錄中嵌套子組件以解決這個問題。好比:
components/
|- TodoList/
   |- Item/
      |- index.vue
      |- Button.vue
   |- index.vue
或:
components/
|- TodoList/
   |- Item/
      |- Button.vue
   |- Item.vue
|- TodoList.vue
可是這種方式並不推薦,由於這會致使:
許多文件的名字相同,使得在編輯器中快速切換文件變得困難。
過多嵌套的子目錄增長了在編輯器側邊欄中瀏覽組件所花的時間。
反例

components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
好例子

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
緊密耦合的組件名

(6)組件名應該以高級別的 (一般是通常化描述的) 單詞開頭,以描述性的修飾詞結尾

組件名中的單詞順序 強烈推薦

組件名應該以高級別的 (一般是通常化描述的) 單詞開頭,以描述性的修飾詞結尾。
 詳解
你可能會疑惑:
「爲何咱們給組件命名時很少聽從天然語言呢?」
在天然的英文裏,形容詞和其它描述語一般都出如今名詞以前,不然須要使用鏈接詞。好比:
Coffee with milk
Soup of the day
Visitor to the museum
若是你願意,你徹底能夠在組件名裏包含這些鏈接詞,可是單詞的順序很重要。
一樣要注意在你的應用中所謂的「高級別」是跟語境有關的。好比對於一個帶搜索表單的應用來講,它可能包含這樣的組件:
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
你可能注意到了,咱們很難看出來哪些組件是針對搜索的。如今咱們來根據規則給組件從新命名:
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue
由於編輯器一般會按字母順序組織文件,因此如今組件之間的重要關係一目瞭然。
你可能想換成多級目錄的方式,把全部的搜索組件放到「search」目錄,把全部的設置組件放到「settings」目錄。咱們只推薦在很是大型 (若有 100+ 個組件) 的應用下才考慮這麼作,由於:
在多級目錄間找來找去,要比在單個 components 目錄下滾動查找要花費更多的精力。
存在組件重名 (好比存在多個 ButtonDelete 組件) 的時候在編輯器裏更難快速定位。
讓重構變得更難,由於爲一個移動了的組件更新相關引用時,查找/替換一般並不高效。
反例

components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
好例子

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
組件名中的單詞順序

(7)在單文件組件、字符串模板和 JSX 中沒有內容的組件應該是自閉合的——但在 DOM 模板裏永遠不要這樣作。

自閉合組件 強烈推薦

在單文件組件、字符串模板和 JSX 中沒有內容的組件應該是自閉合的——但在 DOM 模板裏永遠不要這樣作。
自閉合組件表示它們不只沒有內容,並且刻意沒有內容。其不一樣之處就好像書上的一頁白紙對比貼有「本頁有意留白」標籤的白紙。並且沒有了額外的閉合標籤,你的代碼也更簡潔。
不幸的是,HTML 並不支持自閉合的自定義元素——只有官方的「空」元素。因此上述策略僅適用於進入 DOM 以前 Vue 的模板編譯器可以觸達的地方,而後再產出符合 DOM 規範的 HTML。
反例

<!-- 在單文件組件、字符串模板和 JSX 中 -->
<MyComponent></MyComponent>
<!-- 在 DOM 模板中 -->
<my-component/>
好例子

<!-- 在單文件組件、字符串模板和 JSX 中 -->
<MyComponent/>
<!-- 在 DOM 模板中 -->
<my-component></my-component>
自閉合組件

(8)對於絕大多數項目來講,在單文件組件和字符串模板中組件名應該老是 PascalCase 的——可是在 DOM 模板中老是 kebab-case 的。

模板中的組件名大小寫 強烈推薦

對於絕大多數項目來講,在單文件組件和字符串模板中組件名應該老是 PascalCase 的——可是在 DOM 模板中老是 kebab-case 的。
PascalCase 相比 kebab-case 有一些優點:
編輯器能夠在模板裏自動補全組件名,由於 PascalCase 一樣適用於 JavaScript。
<MyComponent> 視覺上比 <my-component> 更可以和單個單詞的 HTML 元素區別開來,由於前者的不一樣之處有兩個大寫字母,後者只有一個橫線。
若是你在模板中使用任何非 Vue 的自定義元素,好比一個 Web Component,PascalCase 確保了你的 Vue 組件在視覺上仍然是易識別的。
不幸的是,因爲 HTML 是大小寫不敏感的,在 DOM 模板中必須仍使用 kebab-case。
還請注意,若是你已是 kebab-case 的重度用戶,那麼與 HTML 保持一致的命名約定且在多個項目中保持相同的大小寫規則就可能比上述優點更爲重要了。在這些狀況下,在全部的地方都使用 kebab-case 一樣是能夠接受的。
反例

<!-- 在單文件組件和字符串模板中 -->
<mycomponent/>
<!-- 在單文件組件和字符串模板中 -->
<myComponent/>
<!-- 在 DOM 模板中 -->
<MyComponent></MyComponent>
好例子

<!-- 在單文件組件和字符串模板中 -->
<MyComponent/>
<!-- 在 DOM 模板中 -->
<my-component></my-component>
或者
<!-- 在全部地方 -->
<my-component></my-component>
模板中的組件名大小寫

(9)JS/JSX 中的組件名應該始終是 PascalCase 的,儘管在較爲簡單的應用中只使用 Vue.component 進行全局組件註冊時,可使用 kebab-case 字符串。

JS/JSX 中的組件名大小寫 強烈推薦

JS/JSX 中的組件名應該始終是 PascalCase 的,儘管在較爲簡單的應用中只使用 Vue.component 進行全局組件註冊時,可使用 kebab-case 字符串。
 詳解
在 JavaScript 中,PascalCase 是類和構造函數 (本質上任何能夠產生多份不一樣實例的東西) 的命名約定。Vue 組件也有多份實例,因此一樣使用 PascalCase 是有意義的。額外的好處是,在 JSX (和模板) 裏使用 PascalCase 使得代碼的讀者更容易分辨 Vue 組件和 HTML 元素。
然而,對於只經過 Vue.component 定義全局組件的應用來講,咱們推薦 kebab-case 做爲替代。緣由是:
全局組件不多被 JavaScript 引用,因此遵照 JavaScript 的命名約定意義不大。
這些應用每每包含許多 DOM 內的模板,這種狀況下是必須使用 kebab-case 的。
反例

Vue.component('myComponent', {
  // ...
})
import myComponent from './MyComponent.vue'
export default {
  name: 'myComponent',
  // ...
}
export default {
  name: 'my-component',
  // ...
}
好例子

Vue.component('MyComponent', {
  // ...
})
Vue.component('my-component', {
  // ...
})
import MyComponent from './MyComponent.vue'
export default {
  name: 'MyComponent',
  // ...
}
JS/JSX 中的組件名大小寫

(10)組件名應該傾向於完整單詞而不是縮寫。

完整單詞的組件名 強烈推薦

組件名應該傾向於完整單詞而不是縮寫。
編輯器中的自動補全已經讓書寫長命名的代價很是之低了,而其帶來的明確性倒是很是寶貴的。不經常使用的縮寫尤爲應該避免。
反例

components/
|- SdSettings.vue
|- UProfOpts.vue
好例子

components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
完整單詞的組件名

(11)在聲明 prop 的時候,其命名應該始終使用 camelCase,而在模板和 JSX 中應該始終使用 kebab-case。

Prop 名大小寫 強烈推薦

在聲明 prop 的時候,其命名應該始終使用 camelCase,而在模板和 JSX 中應該始終使用 kebab-case。
咱們單純的遵循每一個語言的約定。在 JavaScript 中更天然的是 camelCase。而在 HTML 中則是 kebab-case。
反例

props: {
  'greeting-text': String
}
<WelcomeMessage greetingText="hi"/>
好例子

props: {
  greetingText: String
}
<WelcomeMessage greeting-text="hi"/>
Prop 名大小寫

(12)多個特性的元素應該分多行撰寫,每一個特性一行。

多個特性的元素 強烈推薦

多個特性的元素應該分多行撰寫,每一個特性一行。
在 JavaScript 中,用多行分隔對象的多個屬性是很常見的最佳實踐,由於這樣更易讀。模板和 JSX 值得咱們作相同的考慮。
反例

<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent foo="a" bar="b" baz="c"/>
好例子

<img
  src="https://vuejs.org/images/logo.png"
  alt="Vue Logo"
>
<MyComponent
  foo="a"
  bar="b"
  baz="c"
/>
多個特性的元素

(13)組件模板應該只包含簡單的表達式,複雜的表達式則應該重構爲計算屬性或方法。

模板中簡單的表達式 強烈推薦

組件模板應該只包含簡單的表達式,複雜的表達式則應該重構爲計算屬性或方法。
複雜表達式會讓你的模板變得不那麼聲明式。咱們應該儘可能描述應該出現的是什麼,而非如何計算那個值。並且計算屬性和方法使得代碼能夠重用。
反例

{{
  fullName.split(' ').map(function (word) {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')
}}
好例子

<!-- 在模板中 -->
{{ normalizedFullName }}
// 複雜表達式已經移入一個計算屬性
computed: {
  normalizedFullName: function () {
    return this.fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}
模板中簡單的表達式

(14)應該把複雜計算屬性分割爲儘量多的更簡單的屬性。

簡單的計算屬性 強烈推薦

應該把複雜計算屬性分割爲儘量多的更簡單的屬性。
 詳解
更簡單、命名得當的計算屬性是這樣的:
易於測試
當每一個計算屬性都包含一個很是簡單且不多依賴的表達式時,撰寫測試以確保其正確工做就會更加容易。
易於閱讀
簡化計算屬性要求你爲每個值都起一個描述性的名稱,即使它不可複用。這使得其餘開發者 (以及將來的你) 更容易專一在他們關心的代碼上並搞清楚發生了什麼。
更好的「擁抱變化」
任何可以命名的值均可能用在視圖上。舉個例子,咱們可能打算展現一個信息,告訴用戶他們存了多少錢;也可能打算計算稅費,可是可能會分開展示,而不是做爲總價的一部分。
小的、專一的計算屬性減小了信息使用時的假設性限制,因此需求變動時也用不着那麼多重構了。
反例

computed: {
  price: function () {
    var basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice -
      basePrice * (this.discountPercent || 0)
    )
  }
}
好例子

computed: {
  basePrice: function () {
    return this.manufactureCost / (1 - this.profitMargin)
  },
  discount: function () {
    return this.basePrice * (this.discountPercent || 0)
  },
  finalPrice: function () {
    return this.basePrice - this.discount
  }
}
簡單的計算屬性

(15)非空 HTML 特性值應該始終帶引號 (單引號或雙引號,選你 JS 裏不用的那個)。

帶引號的特性值 強烈推薦

非空 HTML 特性值應該始終帶引號 (單引號或雙引號,選你 JS 裏不用的那個)。
在 HTML 中不帶空格的特性值是能夠沒有引號的,但這樣作經常致使帶空格的特徵值被迴避,致使其可讀性變差。
反例

<input type=text>
<AppSidebar :style={width:sidebarWidth+'px'}>
好例子

<input type="text">
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
帶引號的特性值

(16)指令縮寫 (用 : 表示 v-bind: 和用 @ 表示 v-on:) 應該要麼都用要麼都不用。

指令縮寫 強烈推薦

指令縮寫 (用 : 表示 v-bind: 和用 @ 表示 v-on:) 應該要麼都用要麼都不用。
反例

<input
  v-bind:value="newTodoText"
  :placeholder="newTodoInstructions"
>
<input
  v-on:input="onInput"
  @focus="onFocus"
>
好例子

<input
  :value="newTodoText"
  :placeholder="newTodoInstructions"
>
<input
  v-bind:value="newTodoText"
  v-bind:placeholder="newTodoInstructions"
>
<input
  @input="onInput"
  @focus="onFocus"
>
<input
  v-on:input="onInput"
  v-on:focus="onFocus"
>
指令縮寫

 

優先級 C:推薦

當存在多個一樣好的選項,選任意一個均可以確保一致性。在這些規則裏,咱們描述了每一個選項並建議一個默認的選擇。也就是說只要保持一致且理由充分,你能夠隨意在你的代碼庫中作出不一樣的選擇。請務必給出一個好的理由!經過接受社區的標準,你將會:

  1. 訓練你的大腦,以便更容易的處理你在社區遇到的代碼;
  2. 不作修改就能夠直接複製粘貼社區的代碼示例;
  3. 可以常常招聘到和你編碼習慣相同的新人,至少跟 Vue 相關的東西是這樣的。

(1)組件/實例的選項應該有統一的順序

組件/實例的選項的順序 推薦

組件/實例的選項應該有統一的順序。
這是咱們推薦的組件選項默認順序。它們被劃分爲幾大類,因此你也能知道從插件裏添加的新屬性應該放到哪裏。
反作用 (觸發組件外的影響)
el
全局感知 (要求組件之外的知識)
name
parent
組件類型 (更改組件的類型)
functional
模板修改器 (改變模板的編譯方式)
delimiters
comments
模板依賴 (模板內使用的資源)
components
directives
filters
組合 (向選項裏合併屬性)
extends
mixins
接口 (組件的接口)
inheritAttrs
model
props/propsData
本地狀態 (本地的響應式屬性)
data
computed
事件 (經過響應式事件觸發的回調)
watch
生命週期鉤子 (按照它們被調用的順序)
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
activated
deactivated
beforeDestroy
destroyed
非響應式的屬性 (不依賴響應系統的實例屬性)
methods
渲染 (組件輸出的聲明式描述)
template/render
renderError
組件/實例的選項的順序

(2)元素 (包括組件) 的特性應該有統一的順序。

元素特性的順序 推薦

元素 (包括組件) 的特性應該有統一的順序。
這是咱們爲組件選項推薦的默認順序。它們被劃分爲幾大類,因此你也能知道新添加的自定義特性和指令應該放到哪裏。
定義 (提供組件的選項)
is
列表渲染 (建立多個變化的相同元素)
v-for
條件渲染 (元素是否渲染/顯示)
v-if
v-else-if
v-else
v-show
v-cloak
渲染方式 (改變元素的渲染方式)
v-pre
v-once
全局感知 (須要超越組件的知識)
id
惟一的特性 (須要惟一值的特性)
ref
key
slot
雙向綁定 (把綁定和事件結合起來)
v-model
其它特性 (全部普通的綁定或未綁定的特性)
事件 (組件事件監聽器)
v-on
內容 (覆寫元素的內容)
v-html
v-text
元素特性的順序

(3)你可能想在多個屬性之間增長一個空行,特別是在這些選項一屏放不下,須要滾動才能都看到的時候。

組件/實例選項中的空行 推薦

你可能想在多個屬性之間增長一個空行,特別是在這些選項一屏放不下,須要滾動才能都看到的時候。
當你的組件開始以爲密集或難以閱讀時,在多個屬性之間添加空行可讓其變得容易。在一些諸如 Vim 的編輯器裏,這樣格式化後的選項還能經過鍵盤被快速導航。
好例子

props: {
  value: {
    type: String,
    required: true
  },

  focused: {
    type: Boolean,
    default: false
  },

  label: String,
  icon: String
},

computed: {
  formattedValue: function () {
    // ...
  },

  inputClasses: function () {
    // ...
  }
}
// 沒有空行在組件易於閱讀和導航時也沒問題。
props: {
  value: {
    type: String,
    required: true
  },
  focused: {
    type: Boolean,
    default: false
  },
  label: String,
  icon: String
},
computed: {
  formattedValue: function () {
    // ...
  },
  inputClasses: function () {
    // ...
  }
}
組件/實例選項中的空行

(4)單文件組件應該老是讓 <script>、<template> 和 <style> 標籤的順序保持一致。且 <style> 要放在最後,由於另外兩個標籤至少要有一個。

單文件組件的頂級元素的順序 推薦

單文件組件應該老是讓 <script>、<template> 和 <style> 標籤的順序保持一致。且 <style> 要放在最後,由於另外兩個標籤至少要有一個。
反例

<style>/* ... */</style>
<script>/* ... */</script>
<template>...</template>
<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
好例子

<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>
<!-- ComponentA.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
單文件組件的頂級元素的順序

 

優先級 D:謹慎使用

有些 Vue 特性的存在是爲了照顧極端狀況或幫助老代碼的平穩遷移。當被過分使用時,這些特性會讓你的代碼難於維護甚至變成 bug 的來源。這些規則是爲了給有潛在風險的特性敲個警鐘,並說明它們何時不該該使用以及爲何。

 (1)若是一組 v-if + v-else 的元素類型相同,最好使用 key (好比兩個 <div> 元素)。

沒有在 v-if/v-if-else/v-else 中使用 key 謹慎使用

若是一組 v-if + v-else 的元素類型相同,最好使用 key (好比兩個 <div> 元素)。
默認狀況下,Vue 會盡量高效的更新 DOM。這意味着其在相同類型的元素之間切換時,會修補已存在的元素,而不是將舊的元素移除而後在同一位置添加一個新元素。若是本不相同的元素被識別爲相同,則會出現意料以外的反作用。
反例

<div v-if="error">
  錯誤:{{ error }}
</div>
<div v-else>
  {{ results }}
</div>
好例子

<div
  v-if="error"
  key="search-status"
>
  錯誤:{{ error }}
</div>
<div
  v-else
  key="search-results"
>
  {{ results }}
</div>
<p v-if="error">
  錯誤:{{ error }}
</p>
<div v-else>
  {{ results }}
</div>
沒有在 v-if/v-if-else/v-else 中使用 key

(2)元素選擇器應該避免在 scoped 中出現。

scoped 中的元素選擇器 謹慎使用

元素選擇器應該避免在 scoped 中出現。
在 scoped 樣式中,類選擇器比元素選擇器更好,由於大量使用元素選擇器是很慢的。
 詳解 
反例

<template>
  <button>X</button>
</template>

<style scoped>
button {
  background-color: red;
}
</style>
好例子

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

<style scoped>
.btn-close {
  background-color: red;
}
</style>
scoped 中的元素選擇器

(3)應該優先經過 prop 和事件進行父子組件之間的通訊,而不是 this.$parent 或改變 prop。

隱性的父子組件通訊 謹慎使用

應該優先經過 prop 和事件進行父子組件之間的通訊,而不是 this.$parent 或改變 prop。
一個理想的 Vue 應用是 prop 向下傳遞,事件向上傳遞的。遵循這一約定會讓你的組件更易於理解。然而,在一些邊界狀況下 prop 的變動或 this.$parent 可以簡化兩個深度耦合的組件。
問題在於,這種作法在不少簡單的場景下可能會更方便。但請小心,不要爲了一時方便 (少寫代碼) 而犧牲數據流向的簡潔性 (易於理解)。
反例

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>
  `
})
好例子

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>
  `
})
隱性的父子組件通訊

(4)應該優先經過 Vuex 管理全局狀態,而不是經過 this.$root 或一個全局事件總線。

非 Flux 的全局狀態管理 謹慎使用

應該優先經過 Vuex 管理全局狀態,而不是經過 this.$root 或一個全局事件總線。
經過 this.$root 和/或全局事件總線管理狀態在不少簡單的狀況下都是很方便的,可是並不適用於絕大多數的應用。Vuex 提供的不只是一個管理狀態的中心區域,仍是組織、追蹤和調試狀態變動的好工具。
反例

// 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
      })
    }
  }
})
好例子

// 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>
非 Flux 的全局狀態管理
相關文章
相關標籤/搜索