VUE風(fēng)格指南


title: 風(fēng)格指南
type: style-guide


這里是官方的 Vue 特有代碼的風(fēng)格指南。如果在工程中使用 Vue瞻鹏,為了回避錯誤朦佩、小糾結(jié)和反模式,該指南是份不錯的參考喉刘。不過我們也不確信風(fēng)格指南的所有內(nèi)容對于所有的團(tuán)隊(duì)或工程都是理想的瞧柔。所以根據(jù)過去的經(jīng)驗(yàn)、周圍的技術(shù)棧睦裳、個人價值觀做出有意義的偏差是可取的非剃。

對于其絕大部分,我們也總體上避免就 JavaScript 或 HTML 的本身提出建議推沸。我們不介意你是否使用分號或結(jié)尾的逗號备绽。我們不介意你在 HTML 特性中使用單引號還是雙引號。不過當(dāng)我們發(fā)現(xiàn)在 Vue 的情景下有幫助的特定模式時鬓催,也會存在例外肺素。

不久之后,我們還會提供操作層面的技巧宇驾。有的時候你只需要遵守規(guī)則倍靡,而我們會盡可能向你展示如何使用 ESLint 及其它自動化程序把操作層面弄得更簡單。

最終课舍,我們把所有的規(guī)則歸為了四個大類:

規(guī)則歸類

優(yōu)先級 A:必要的

這些規(guī)則會幫你規(guī)避錯誤塌西,所以學(xué)習(xí)并接受它們帶來的全部代價吧他挎。這里面可能存在例外,但應(yīng)該非常少捡需,且只有你同時精通 JavaScript 和 Vue 才可以這樣做办桨。

優(yōu)先級 B:強(qiáng)烈推薦

這些規(guī)則能夠在絕大多數(shù)工程中改善可讀性和開發(fā)體驗(yàn)。即使你違反了站辉,代碼還是能照常運(yùn)行呢撞,但例外應(yīng)該盡可能少且有合理的理由。

優(yōu)先級 C:推薦

當(dāng)存在多個同樣好的選項(xiàng)饰剥,選任意一個都可以確保一致性殊霞。在這些規(guī)則里,我們描述了每個選項(xiàng)并建議一個默認(rèn)的選擇汰蓉。也就是說只要保持一致且理由充分绷蹲,你可以隨意在你的代碼庫中做出不同的選擇。請務(wù)必給出一個好的理由顾孽!通過接受社區(qū)的標(biāo)準(zhǔn)祝钢,你將會:

  1. 訓(xùn)練你的大腦,以便更容易的處理你在社區(qū)遇到的代碼岩齿;
  2. 不做修改就可以直接復(fù)制粘貼社區(qū)的代碼示例太颤;
  3. 能夠經(jīng)常招聘到和你編碼習(xí)慣相同的新人,至少跟 Vue 相關(guān)的東西是這樣的盹沈。

優(yōu)先級 D:謹(jǐn)慎使用

有些 Vue 特性的存在是為了照顧極端情況或幫助老代碼的平穩(wěn)遷移龄章。當(dāng)被過度使用時,這些特性會讓你的代碼難于維護(hù)甚至變成 bug 的來源乞封。這些規(guī)則是為了給有潛在風(fēng)險的特性敲個警鐘做裙,并說明它們什么時候不應(yīng)該使用以及為什么。

優(yōu)先級 A 的規(guī)則:必要的 (規(guī)避錯誤)

組件名為多個單詞 <sup data-p="a">必要

組件名應(yīng)該始終是多個單詞的肃晚,根組件 App 除外锚贱。

這樣做可以避免跟現(xiàn)有的以及未來的 HTML 元素相沖突,因?yàn)樗械?HTML 元素名稱都是單個單詞的关串。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

Vue.component('todo-item', {
  // ...
})
export default {
  name: 'TodoItem',
  // ...
}

{% raw %}</div>{% endraw %}

組件數(shù)據(jù) <sup data-p="a">必要

組件的 data 必須是一個函數(shù)拧廊。

當(dāng)在組件中使用 data 屬性的時候 (除了 new Vue 外的任何地方),它的值必須是返回一個對象的函數(shù)晋修。

此處:返回一個唯一的對象吧碾,不要和其他組件共用一個對象進(jìn)行返回(這樣就不會處理同一個對象)

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

當(dāng) data 的值是一個對象時,它會在這個組件的所有實(shí)例之間共享墓卦。想象一下倦春,假如一個 TodoList 組件的數(shù)據(jù)是這樣的:

data: {
  listTitle: '',
  todos: []
}

我們可能希望重用這個組件,允許用戶維護(hù)多個列表 (比如分為購物、心愿單睁本、日常事務(wù)等)尿庐。這時就會產(chǎn)生問題。因?yàn)槊總€組件的實(shí)例都引用了相同的數(shù)據(jù)對象呢堰,更改其中一個列表的標(biāo)題就會改變其它每一個列表的標(biāo)題抄瑟。增刪改一個待辦事項(xiàng)的時候也是如此。

取而代之的是暮胧,我們希望每個組件實(shí)例都管理其自己的數(shù)據(jù)锐借。為了做到這一點(diǎn)问麸,每個實(shí)例必須生成一個獨(dú)立的數(shù)據(jù)對象往衷。在 JavaScript 中,在一個函數(shù)中返回這個對象就可以了:

data: function () {
  return {
    listTitle: '',
    todos: []
  }
}

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

Vue.component('some-comp', {
  data: function () {
    return {
      foo: 'bar'
    }
  }
})
// In a .vue file
export default {
  data () {
    return {
      foo: 'bar'
    }
  }
}
// 在一個 Vue 的根實(shí)例上直接使用對象是可以的严卖,
// 因?yàn)橹淮嬖谝粋€這樣的實(shí)例席舍。
new Vue({
  data: {
    foo: 'bar'
  }
})

{% raw %}</div>{% endraw %}

Prop 定義 <sup data-p="a">必要

Prop 定義應(yīng)該盡量詳細(xì)。

在你提交的代碼中哮笆,prop 的定義應(yīng)該盡量詳細(xì)来颤,至少需要指定其類型。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

細(xì)致的 prop 定義有兩個好處:

  • 它們寫明了組件的 API稠肘,所以很容易看懂組件的用法福铅;
  • 在開發(fā)環(huán)境下,如果向一個組件提供格式不正確的 prop项阴,Vue 將會告警滑黔,以幫助你捕獲潛在的錯誤來源。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

// 這樣做只有開發(fā)原型系統(tǒng)時可以接受
props: ['status']

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

{% raw %}</div>{% endraw %}

v-for 設(shè)置鍵值 <sup data-p="a">必要

總是用 key 配合 v-for略荡。

在組件上總是必須用 key 配合 v-for,以便維護(hù)內(nèi)部組件及其子樹的狀態(tài)歉胶。甚至在元素上維護(hù)可預(yù)測的行為汛兜,比如動畫中的對象固化 (object constancy),也是一種好的做法通今。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

假設(shè)你有一個待辦事項(xiàng)列表:

data: function () {
  return {
    todos: [
      {
        id: 1,
        text: '學(xué)習(xí)使用 v-for'
      },
      {
        id: 2,
        text: '學(xué)習(xí)使用 key'
      }
    ]
  }
}

然后你把它們按照字母順序排序粥谬。在更新 DOM 的時候,Vue 將會優(yōu)化渲染把可能的 DOM 變動降到最低辫塌。即可能刪掉第一個待辦事項(xiàng)元素漏策,然后把它重新加回到列表的最末尾。

這里的問題在于璃氢,不要刪除仍然會留在 DOM 中的元素哟玷。比如你想使用 <transition-group> 給列表加過渡動畫,或想在被渲染元素是 <input> 時保持聚焦。在這些情況下巢寡,為每一個項(xiàng)目添加一個唯一的鍵值 (比如 :key="todo.id") 將會讓 Vue 知道如何使行為更容易預(yù)測喉脖。

根據(jù)我們的經(jīng)驗(yàn),最好始終添加一個唯一的鍵值抑月,以便你和你的團(tuán)隊(duì)永遠(yuǎn)不必?fù)?dān)心這些極端情況树叽。也在少數(shù)對性能有嚴(yán)格要求的情況下,為了避免對象固化谦絮,你可以刻意做一些非常規(guī)的處理题诵。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

{% raw %}</div>{% endraw %}

避免 v-ifv-for 用在一起 <sup data-p="a">必要

永遠(yuǎn)不要把 v-ifv-for 同時用在同一個元素上。

一般我們在兩種常見的情況下會傾向于這樣做:

  • 為了過濾一個列表中的項(xiàng)目 (比如 v-for="user in users" v-if="user.isActive")层皱。在這種情形下性锭,請將 users 替換為一個計算屬性 (比如 activeUsers),讓其返回過濾后的列表叫胖。

  • 為了避免渲染本應(yīng)該被隱藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")草冈。這種情形下,請將 v-if 移動至容器元素上 (比如 ul, ol)瓮增。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

當(dāng) Vue 處理指令時怎棱,v-forv-if 具有更高的優(yōu)先級,所以這個模板:

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

將會經(jīng)過如下運(yùn)算:

this.users.map(function (user) {
  if (user.isActive) {
    return user.name
  }
})

因此哪怕我們只渲染出一小部分用戶的元素绷跑,也得在每次重渲染的時候遍歷整個列表拳恋,不論活躍用戶是否發(fā)生了變化。

通過將其更換為在如下的一個計算屬性上遍歷:

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 數(shù)組發(fā)生相關(guān)變化時才被重新運(yùn)算砸捏,過濾更高效谬运。
  • 使用 v-for="user in activeUsers" 之后,我們在渲染的時候遍歷活躍用戶带膜,渲染更高效吩谦。
  • 解藕渲染層的邏輯,可維護(hù)性 (對邏輯的更改和擴(kuò)展) 更強(qiáng)膝藕。

為了獲得同樣的好處式廷,我們也可以把:

<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 為否的時候運(yùn)算 v-for袜爪。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

<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>

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

<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>

{% raw %}</div>{% endraw %}

為組件樣式設(shè)置作用域 <sup data-p="a">必要

對于應(yīng)用來說蠕趁,頂級 App 組件和布局組件中的樣式可以是全局的,但是其它所有組件都應(yīng)該是有作用域的辛馆。

這條規(guī)則只和單文件組件有關(guān)俺陋。你不一定要使用 scoped 特性豁延。設(shè)置作用域也可以通過 CSS Modules,那是一個基于 class 的類似 BEM 的策略腊状,當(dāng)然你也可以使用其它的庫或約定诱咏。

不管怎樣,對于組件庫缴挖,我們應(yīng)該更傾向于選用基于 class 的策略而不是 scoped 特性袋狞。

這讓覆寫內(nèi)部樣式更容易:使用了常人可理解的 class 名稱且沒有太高的選擇器優(yōu)先級,而且不太會導(dǎo)致沖突映屋。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

如果你和其他開發(fā)者一起開發(fā)一個大型工程苟鸯,或有時引入三方 HTML/CSS (比如來自 Auth0),設(shè)置一致的作用域會確保你的樣式只會運(yùn)用在它們想要作用的組件上棚点。

不止要使用 scoped 特性早处,使用唯一的 class 名可以幫你確保那些三方庫的 CSS 不會運(yùn)用在你自己的 HTML 上。比如許多工程都使用了 button乙濒、btnicon class 名陕赃,所以即便你不使用類似 BEM 的策略卵蛉,添加一個 app 專屬或組件專屬的前綴 (比如 ButtonClose-icon) 也可以提供很多保護(hù)颁股。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

<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>

{% raw %}</div>{% endraw %}

私有屬性名 <sup data-p="a">必要

在插件、混入等擴(kuò)展中始終為自定義的私有屬性使用 $_ 前綴傻丝。并附帶一個命名空間以回避和其它作者的沖突 (比如 $_yourPluginName_)甘有。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

Vue 使用 _ 前綴來定義其自身的私有屬性,所以使用相同的前綴 (比如 _update) 有覆寫實(shí)例屬性的風(fēng)險葡缰。即便你檢查確認(rèn) Vue 當(dāng)前版本沒有用到這個屬性名拘悦,也不能保證和將來的版本沒有沖突茬高。

對于 $ 前綴來說,其在 Vue 生態(tài)系統(tǒng)中的目的是暴露給用戶的一個特殊的實(shí)例屬性,所以把它用于私有屬性并不合適芋齿。

不過,我們推薦把這兩個前綴結(jié)合為 $_了牛,作為一個用戶定義的私有屬性的約定鹰贵,以確保不會和 Vue 自身相沖突。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

var myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update: function () {
      // ...
    }
  }
}

{% raw %}</div>{% endraw %}

優(yōu)先級 B 的規(guī)則:強(qiáng)烈推薦 (增強(qiáng)可讀性)

組件文件 <sup data-p="b">強(qiáng)烈推薦

只要有能夠拼接文件的構(gòu)建系統(tǒng)茄茁,就把每個組件單獨(dú)分成文件魂贬。

當(dāng)你需要編輯一個組件或查閱一個組件的用法時,可以更快速的找到它裙顽。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

components/
|- TodoList.js
|- TodoItem.js
components/
|- TodoList.vue
|- TodoItem.vue

{% raw %}</div>{% endraw %}

單文件組件文件的大小寫 <sup data-p="b">強(qiáng)烈推薦

單文件組件的文件名應(yīng)該要么始終是單詞大寫開頭 (PascalCase)付燥,要么始終是橫線連接 (kebab-case)。

單詞大寫開頭對于代碼編輯器的自動補(bǔ)全最為友好愈犹,因?yàn)檫@使得我們在 JS(X) 和模板中引用組件的方式盡可能的一致键科。然而,混用文件命名方式有的時候會導(dǎo)致大小寫不敏感的文件系統(tǒng)的問題,這也是橫線連接命名同樣完全可取的原因勋颖。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

components/
|- mycomponent.vue
components/
|- myComponent.vue

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

components/
|- MyComponent.vue
components/
|- my-component.vue

{% raw %}</div>{% endraw %}

基礎(chǔ)組件名 <sup data-p="b">強(qiáng)烈推薦

應(yīng)用特定樣式和約定的基礎(chǔ)組件 (也就是展示類的梆掸、無邏輯的或無狀態(tài)的組件) 應(yīng)該全部以一個特定的前綴開頭,比如 Base牙言、AppV酸钦。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

這些組件為你的應(yīng)用奠定了一致的基礎(chǔ)樣式和行為。它們可能包括:

  • HTML 元素
  • 其它帶 Base 前綴的組件
  • 第三方 UI 組件庫

但是它們絕不會包括全局狀態(tài) (比如來自 Vuex store)咱枉。

它們的名字通常包含所包裹元素的名字 (比如 BaseButton卑硫、BaseTable),除非沒有現(xiàn)成的對應(yīng)功能的元素 (比如 BaseIcon)蚕断。如果你為特定的上下文構(gòu)建類似的組件欢伏,那它們幾乎總會消費(fèi)這些組件 (比如 BaseButton 可能會用在 ButtonSubmit 上)。

這樣做的幾個好處:

  • 當(dāng)你在編輯器中以字母順序排序時亿乳,你的應(yīng)用的基礎(chǔ)組件會全部列在一起硝拧,這樣更容易識別。

  • 因?yàn)榻M件名應(yīng)該始終是多個單詞葛假,所以這樣做可以避免你在包裹簡單組件時隨意選擇前綴 (比如 MyButton障陶、VueButton)。

  • 因?yàn)檫@些組件會被頻繁使用聊训,所以你可能想把它們放到全局而不是在各處分別導(dǎo)入它們抱究。使用相同的前綴可以讓 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)
    })
    

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue

{% raw %}</div>{% endraw %}

單例組件名 <sup data-p="b">強(qiáng)烈推薦

只應(yīng)該擁有單個活躍實(shí)例的組件應(yīng)該以 The 前綴命名,以示其唯一性带斑。

這不意味著組件只可用于一個單頁面鼓寺,而是每個頁面只使用一次。這些組件永遠(yuǎn)不接受任何 prop勋磕,因?yàn)樗鼈兪菫槟愕膽?yīng)用定制的妈候,而不是它們在你的應(yīng)用中的上下文。如果你發(fā)現(xiàn)有必要添加 prop挂滓,那就表明這實(shí)際上是一個可復(fù)用的組件苦银,只是目前在每個頁面里只使用一次。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

components/
|- Heading.vue
|- MySidebar.vue

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

components/
|- TheHeading.vue
|- TheSidebar.vue

{% raw %}</div>{% endraw %}

緊密耦合的組件名 <sup data-p="b">強(qiáng)烈推薦

和父組件緊密耦合的子組件應(yīng)該以父組件名作為前綴命名杂彭。

如果一個組件只在某個父組件的場景下有意義墓毒,這層關(guān)系應(yīng)該體現(xiàn)在其名字上。因?yàn)榫庉嬈魍ǔ醋帜疙樞蚪M織文件亲怠,所以這樣做可以把相關(guān)聯(lián)的文件排在一起所计。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

你可以試著通過在其父組件命名的目錄中嵌套子組件以解決這個問題。比如:

components/
|- TodoList/
   |- Item/
      |- index.vue
      |- Button.vue
   |- index.vue

或:

components/
|- TodoList/
   |- Item/
      |- Button.vue
   |- Item.vue
|- TodoList.vue

但是這種方式并不推薦团秽,因?yàn)檫@會導(dǎo)致:

  • 許多文件的名字相同主胧,使得在編輯器中快速切換文件變得困難叭首。
  • 過多嵌套的子目錄增加了在編輯器側(cè)邊欄中瀏覽組件所花的時間。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue

{% raw %}</div>{% endraw %}

組件名中的單詞順序 <sup data-p="b">強(qiáng)烈推薦

組件名應(yīng)該以高級別的 (通常是一般化描述的) 單詞開頭踪栋,以描述性的修飾詞結(jié)尾焙格。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

你可能會疑惑:

“為什么我們給組件命名時不多遵從自然語言呢?”

在自然的英文里夷都,形容詞和其它描述語通常都出現(xiàn)在名詞之前眷唉,否則需要使用連接詞。比如:

  • Coffee with milk
  • Soup of the day
  • Visitor to the museum

如果你愿意囤官,你完全可以在組件名里包含這些連接詞冬阳,但是單詞的順序很重要。

同樣要注意在你的應(yīng)用中所謂的“高級別”是跟語境有關(guān)的党饮。比如對于一個帶搜索表單的應(yīng)用來說肝陪,它可能包含這樣的組件:

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

你可能注意到了,我們很難看出來哪些組件是針對搜索的⌒趟常現(xiàn)在我們來根據(jù)規(guī)則給組件重新命名:

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue

因?yàn)榫庉嬈魍ǔ醋帜疙樞蚪M織文件氯窍,所以現(xiàn)在組件之間的重要關(guān)系一目了然。

你可能想換成多級目錄的方式蹲堂,把所有的搜索組件放到“search”目錄狼讨,把所有的設(shè)置組件放到“settings”目錄。我們只推薦在非常大型 (如有 100+ 個組件) 的應(yīng)用下才考慮這么做贯城,因?yàn)椋?/p>

  • 在多級目錄間找來找去熊楼,要比在單個 components 目錄下滾動查找要花費(fèi)更多的精力。
  • 存在組件重名 (比如存在多個 ButtonDelete 組件) 的時候在編輯器里更難快速定位能犯。
  • 讓重構(gòu)變得更難,因?yàn)闉橐粋€移動了的組件更新相關(guān)引用時犬耻,查找/替換通常并不高效踩晶。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue

{% raw %}</div>{% endraw %}

自閉合組件 <sup data-p="b">強(qiáng)烈推薦

單文件組件、字符串模板和 JSX 中沒有內(nèi)容的組件應(yīng)該是自閉合的——但在 DOM 模板里永遠(yuǎn)不要這樣做枕磁。

自閉合組件表示它們不僅沒有內(nèi)容渡蜻,而且刻意沒有內(nèi)容。其不同之處就好像書上的一頁白紙對比貼有“本頁有意留白”標(biāo)簽的白紙计济。而且沒有了額外的閉合標(biāo)簽茸苇,你的代碼也更簡潔。

不幸的是沦寂,HTML 并不支持自閉合的自定義元素——只有官方的“空”元素学密。所以上述策略僅適用于進(jìn)入 DOM 之前 Vue 的模板編譯器能夠觸達(dá)的地方,然后再產(chǎn)出符合 DOM 規(guī)范的 HTML传藏。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

{% raw %}</div>{% endraw %}

模板中的組件名大小寫 <sup data-p="b">強(qiáng)烈推薦

對于絕大多數(shù)項(xiàng)目來說,在單文件組件和字符串模板中組件名應(yīng)該總是 PascalCase 的——但是在 DOM 模板中總是 kebab-case 的哭靖。

PascalCase 相比 kebab-case 有一些優(yōu)勢:

  • 編輯器可以在模板里自動補(bǔ)全組件名具垫,因?yàn)?PascalCase 同樣適用于 JavaScript。
  • <MyComponent> 視覺上比 <my-component> 更能夠和單個單詞的 HTML 元素區(qū)別開來试幽,因?yàn)榍罢叩牟煌幱袃蓚€大寫字母筝蚕,后者只有一個橫線。
  • 如果你在模板中使用任何非 Vue 的自定義元素铺坞,比如一個 Web Component饰及,PascalCase 確保了你的 Vue 組件在視覺上仍然是易識別的。

不幸的是康震,由于 HTML 是大小寫不敏感的燎含,在 DOM 模板中必須仍使用 kebab-case。

還請注意腿短,如果你已經(jīng)是 kebab-case 的重度用戶屏箍,那么與 HTML 保持一致的命名約定且在多個項(xiàng)目中保持相同的大小寫規(guī)則就可能比上述優(yōu)勢更為重要了。在這些情況下橘忱,在所有的地方都使用 kebab-case 同樣是可以接受的赴魁。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

或者

<!-- 在所有地方 -->
<my-component></my-component>

{% raw %}</div>{% endraw %}

JS/JSX 中的組件名大小寫 <sup data-p="b">強(qiáng)烈推薦

JS/JSX 中的組件名應(yīng)該始終是 PascalCase 的,盡管在較為簡單的應(yīng)用中只使用 Vue.component 進(jìn)行全局組件注冊時钝诚,可以使用 kebab-case 字符串颖御。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

在 JavaScript 中,PascalCase 是類和構(gòu)造函數(shù) (本質(zhì)上任何可以產(chǎn)生多份不同實(shí)例的東西) 的命名約定凝颇。Vue 組件也有多份實(shí)例潘拱,所以同樣使用 PascalCase 是有意義的。額外的好處是拧略,在 JSX (和模板) 里使用 PascalCase 使得代碼的讀者更容易分辨 Vue 組件和 HTML 元素芦岂。

然而,對于通過 Vue.component 定義全局組件的應(yīng)用來說垫蛆,我們推薦 kebab-case 作為替代禽最。原因是:

  • 全局組件很少被 JavaScript 引用,所以遵守 JavaScript 的命名約定意義不大袱饭。
  • 這些應(yīng)用往往包含許多 DOM 內(nèi)的模板川无,這種情況下是必須使用 kebab-case 的。
    {% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

{% raw %}</div>{% endraw %}

完整單詞的組件名 <sup data-p="b">強(qiáng)烈推薦

組件名應(yīng)該傾向于完整單詞而不是縮寫虑乖。

編輯器中的自動補(bǔ)全已經(jīng)讓書寫長命名的代價非常之低了懦趋,而其帶來的明確性卻是非常寶貴的。不常用的縮寫尤其應(yīng)該避免决左。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

components/
|- SdSettings.vue
|- UProfOpts.vue

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

{% raw %}</div>{% endraw %}

Prop 名大小寫 <sup data-p="b">強(qiáng)烈推薦

在聲明 prop 的時候愕够,其命名應(yīng)該始終使用 camelCase走贪,而在模板和 JSX 中應(yīng)該始終使用 kebab-case。

我們單純的遵循每個語言的約定惑芭。在 JavaScript 中更自然的是 camelCase坠狡。而在 HTML 中則是 kebab-case。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

{% raw %}</div>{% endraw %}

多個特性的元素 <sup data-p="b">強(qiáng)烈推薦

多個特性的元素應(yīng)該分多行撰寫遂跟,每個特性一行逃沿。

在 JavaScript 中,用多行分隔對象的多個屬性是很常見的最佳實(shí)踐幻锁,因?yàn)檫@樣更易讀凯亮。模板和 JSX 值得我們做相同的考慮。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

{% raw %}</div>{% endraw %}

模板中簡單的表達(dá)式 <sup data-p="b">強(qiáng)烈推薦

組件模板應(yīng)該只包含簡單的表達(dá)式哄尔,復(fù)雜的表達(dá)式則應(yīng)該重構(gòu)為計算屬性或方法假消。

復(fù)雜表達(dá)式會讓你的模板變得不那么聲明式。我們應(yīng)該盡量描述應(yīng)該出現(xiàn)的是什么岭接,而非如何計算那個值富拗。而且計算屬性和方法使得代碼可以重用。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

<!-- 在模板中 -->
{{ normalizedFullName }}
// 復(fù)雜表達(dá)式已經(jīng)移入一個計算屬性
computed: {
  normalizedFullName: function () {
    return this.fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}

{% raw %}</div>{% endraw %}

簡單的計算屬性 <sup data-p="b">強(qiáng)烈推薦

應(yīng)該把復(fù)雜計算屬性分割為盡可能多的更簡單的屬性鸣戴。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

更簡單啃沪、命名得當(dāng)?shù)挠嬎銓傩允沁@樣的:

  • 易于測試

    當(dāng)每個計算屬性都包含一個非常簡單且很少依賴的表達(dá)式時,撰寫測試以確保其正確工作就會更加容易窄锅。

  • 易于閱讀

    簡化計算屬性要求你為每一個值都起一個描述性的名稱创千,即便它不可復(fù)用。這使得其他開發(fā)者 (以及未來的你) 更容易專注在他們關(guān)心的代碼上并搞清楚發(fā)生了什么入偷。

  • 更好的“擁抱變化”

    任何能夠命名的值都可能用在視圖上追驴。舉個例子,我們可能打算展示一個信息盯串,告訴用戶他們存了多少錢氯檐;也可能打算計算稅費(fèi),但是可能會分開展現(xiàn)体捏,而不是作為總價的一部分。

    小的糯崎、專注的計算屬性減少了信息使用時的假設(shè)性限制几缭,所以需求變更時也用不著那么多重構(gòu)了。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

computed: {
  basePrice: function () {
    return this.manufactureCost / (1 - this.profitMargin)
  },
  discount: function () {
    return this.basePrice * (this.discountPercent || 0)
  },
  finalPrice: function () {
    return this.basePrice - this.discount
  }
}

{% raw %}</div>{% endraw %}

帶引號的特性值 <sup data-p="b">強(qiáng)烈推薦

非空 HTML 特性值應(yīng)該始終帶引號 (單引號或雙引號沃呢,選你 JS 里不用的那個)年栓。

在 HTML 中不帶空格的特性值是可以沒有引號的,但這樣做常常導(dǎo)致帶空格的特征值被回避薄霜,導(dǎo)致其可讀性變差某抓。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

{% raw %}</div>{% endraw %}

指令縮寫 <sup data-p="b">強(qiáng)烈推薦

指令縮寫 (用 : 表示 v-bind: 和用 @ 表示 v-on:) 應(yīng)該要么都用要么都不用纸兔。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

<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"
>

{% raw %}</div>{% endraw %}

優(yōu)先級 C 的規(guī)則:推薦 (將選擇和認(rèn)知成本最小化)

組件/實(shí)例的選項(xiàng)的順序 <sup data-p="c">推薦

組件/實(shí)例的選項(xiàng)應(yīng)該有統(tǒng)一的順序。

這是我們推薦的組件選項(xiàng)默認(rèn)順序否副。它們被劃分為幾大類汉矿,所以你也能知道從插件里添加的新屬性應(yīng)該放到哪里。

  1. 副作用 (觸發(fā)組件外的影響)
  • el
  1. 全局感知 (要求組件以外的知識)
  • name
  • parent
  1. 組件類型 (更改組件的類型)
  • functional
  1. 模板修改器 (改變模板的編譯方式)
  • delimiters
  • comments
  1. 模板依賴 (模板內(nèi)使用的資源)
  • components
  • directives
  • filters
  1. 組合 (向選項(xiàng)里合并屬性)
  • extends
  • mixins
  1. 接口 (組件的接口)
  • inheritAttrs
  • model
  • props/propsData
  1. 本地狀態(tài) (本地的響應(yīng)式屬性)
  • data
  • computed
  1. 事件 (通過響應(yīng)式事件觸發(fā)的回調(diào))
  • watch
  • 生命周期鉤子 (按照它們被調(diào)用的順序)
  1. 非響應(yīng)式的屬性 (不依賴響應(yīng)系統(tǒng)的實(shí)例屬性)
  • methods
  1. 渲染 (組件輸出的聲明式描述)
  • template/render
  • renderError

元素特性的順序 <sup data-p="c">推薦

元素 (包括組件) 的特性應(yīng)該有統(tǒng)一的順序备禀。

這是我們?yōu)榻M件選項(xiàng)推薦的默認(rèn)順序洲拇。它們被劃分為幾大類,所以你也能知道新添加的自定義特性和指令應(yīng)該放到哪里曲尸。

  1. 定義 (提供組件的選項(xiàng))
  • is
  1. 列表渲染 (創(chuàng)建多個變化的相同元素)
  • v-for
  1. 條件渲染 (元素是否渲染/顯示)
  • v-if
  • v-else-if
  • v-else
  • v-show
  • v-cloak
  1. 渲染方式 (改變元素的渲染方式)
  • v-pre
  • v-once
  1. 全局感知 (需要超越組件的知識)
  • id
  1. 唯一的特性 (需要唯一值的特性)
  • ref
  • key
  • slot
  1. 雙向綁定 (把綁定和事件結(jié)合起來)
  • v-model
  1. 其它特性 (所有普通的綁定或未綁定的特性)

  2. 事件 (組件事件監(jiān)聽器)

  • v-on
  1. 內(nèi)容 (覆寫元素的內(nèi)容)
  • v-html
  • v-text

組件/實(shí)例選項(xiàng)中的空行 <sup data-p="c">推薦

你可能想在多個屬性之間增加一個空行赋续,特別是在這些選項(xiàng)一屏放不下,需要滾動才能都看到的時候另患。

當(dāng)你的組件開始覺得密集或難以閱讀時纽乱,在多個屬性之間添加空行可以讓其變得容易。在一些諸如 Vim 的編輯器里昆箕,這樣格式化后的選項(xiàng)還能通過鍵盤被快速導(dǎo)航鸦列。

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

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

  label: String,
  icon: String
},

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

  inputClasses: function () {
    // ...
  }
}
// 沒有空行在組件易于閱讀和導(dǎo)航時也沒問題。
props: {
  value: {
    type: String,
    required: true
  },
  focused: {
    type: Boolean,
    default: false
  },
  label: String,
  icon: String
},
computed: {
  formattedValue: function () {
    // ...
  },
  inputClasses: function () {
    // ...
  }
}

{% raw %}</div>{% endraw %}

單文件組件的頂級元素的順序 <sup data-p="c">推薦

單文件組件應(yīng)該總是讓 <script>为严、<template><style> 標(biāo)簽的順序保持一致敛熬。且 <style> 要放在最后,因?yàn)榱硗鈨蓚€標(biāo)簽至少要有一個第股。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

{% raw %}</div>{% endraw %}

優(yōu)先級 D 的規(guī)則:謹(jǐn)慎使用 (有潛在危險的模式)

沒有在 v-if/v-if-else/v-else 中使用 key <sup data-p="d">謹(jǐn)慎使用

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

默認(rèn)情況下夕吻,Vue 會盡可能高效的更新 DOM诲锹。這意味著其在相同類型的元素之間切換時,會修補(bǔ)已存在的元素涉馅,而不是將舊的元素移除然后在同一位置添加一個新元素归园。如果本不相同的元素被識別為相同,則會出現(xiàn)意料之外的副作用稚矿。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

<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>

{% raw %}</div>{% endraw %}

scoped 中的元素選擇器 <sup data-p="d">謹(jǐn)慎使用

元素選擇器應(yīng)該避免在 scoped 中出現(xiàn)庸诱。

scoped 樣式中,類選擇器比元素選擇器更好晤揣,因?yàn)榇罅渴褂迷剡x擇器是很慢的桥爽。

{% raw %}
<details>
<summary>
<h4>詳解</h4>
</summary>
{% endraw %}

為了給樣式設(shè)置作用域,Vue 會為元素添加一個獨(dú)一無二的特性昧识,例如 data-v-f3f3eg9钠四。然后修改選擇器,使得在匹配選擇器的元素中跪楞,只有帶這個特性才會真正生效 (比如 button[data-v-f3f3eg9])缀去。

問題在于大量的元素和特性組合的選擇器 (比如 button[data-v-f3f3eg9]) 會比類和特性組合的選擇器 慢侣灶,所以應(yīng)該盡可能選用類選擇器。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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

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

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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

<style scoped>
.btn-close {
  background-color: red;
}
</style>

{% raw %}</div>{% endraw %}

隱性的父子組件通信 <sup data-p="d">謹(jǐn)慎使用

應(yīng)該優(yōu)先通過 prop 和事件進(jìn)行父子組件之間的通信缕碎,而不是 this.$parent 或改變 prop褥影。

一個理想的 Vue 應(yīng)用是 prop 向下傳遞,事件向上傳遞的阎曹。遵循這一約定會讓你的組件更易于理解伪阶。然而,在一些邊界情況下 prop 的變更或 this.$parent 能夠簡化兩個深度耦合的組件处嫌。

問題在于栅贴,這種做法在很多簡單的場景下可能會更方便。但請當(dāng)心熏迹,不要為了一時方便 (少寫代碼) 而犧牲數(shù)據(jù)流向的簡潔性 (易于理解)檐薯。

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

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>
  `
})

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

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>
  `
})

{% raw %}</div>{% endraw %}

非 Flux 的全局狀態(tài)管理 <sup data-p="d">謹(jǐn)慎使用

應(yīng)該優(yōu)先通過 Vuex 管理全局狀態(tài)腾它,而不是通過 this.$root 或一個全局事件總線做个。

通過 this.$root 和/或全局事件總線管理狀態(tài)在很多簡單的情況下都是很方便的,但是并不適用于絕大多數(shù)的應(yīng)用胡诗。Vuex 提供的不僅是一個管理狀態(tài)的中心區(qū)域捆昏,還是組織赚楚、追蹤和調(diào)試狀態(tài)變更的好工具。

{% raw %}</details>{% endraw %}

{% raw %}<div class="style-example example-bad">{% endraw %}

反例

// 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
      })
    }
  }
})

{% raw %}</div>{% endraw %}

{% raw %}<div class="style-example example-good">{% endraw %}

好例子

// 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>

{% raw %}</div>{% endraw %}

{% raw %}
<script>
(function () {
var enforcementTypes = {
none: '<span title="這一規(guī)則無法強(qiáng)制執(zhí)行">自律</span>',
runtime: '運(yùn)行時錯誤',
linter: '<a target="_blank">plugin:vue/recommended</a>'
}
Vue.component('sg-enforcement', {
template: '
<span>
<strong>強(qiáng)制執(zhí)行</strong>:
<span class="style-rule-tag" v-html="humanType"/>
</span>
',
props: {
type: {
type: String,
required: true,
validate: function (value) {
Object.keys(enforcementTypes).indexOf(value) !== -1
}
}
},
computed: {
humanType: function () {
return enforcementTypes[this.type]
}
}
})

// new Vue({
// el: '#main'
// })
})()
</script>
{% endraw %}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骗卜,一起剝皮案震驚了整個濱河市宠页,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寇仓,老刑警劉巖举户,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異遍烦,居然都是意外死亡俭嘁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門服猪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來供填,“玉大人,你說我怎么就攤上這事罢猪〔端洌” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵坡脐,是天一觀的道長。 經(jīng)常有香客問我房揭,道長备闲,這世上最難降的妖魔是什么晌端? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮恬砂,結(jié)果婚禮上咧纠,老公的妹妹穿的比我還像新娘。我一直安慰自己泻骤,他們只是感情好漆羔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狱掂,像睡著了一般演痒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上趋惨,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天鸟顺,我揣著相機(jī)與錄音,去河邊找鬼器虾。 笑死讯嫂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的兆沙。 我是一名探鬼主播欧芽,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼葛圃!你這毒婦竟也來了千扔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤装悲,失蹤者是張志新(化名)和其女友劉穎昏鹃,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诀诊,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡洞渤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了属瓣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片载迄。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖抡蛙,靈堂內(nèi)的尸體忽然破棺而出护昧,到底是詐尸還是另有隱情,我是刑警寧澤粗截,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布惋耙,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏绽榛。R本人自食惡果不足惜湿酸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望灭美。 院中可真熱鬧推溃,春花似錦、人聲如沸届腐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽犁苏。三九已至硬萍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間傀顾,已是汗流浹背襟铭。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留短曾,地道東北人寒砖。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像嫉拐,于是被迫代替她去往敵國和親哩都。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Vue 官方在最近發(fā)布了代碼風(fēng)格指南的BETA版本婉徘,旨在引導(dǎo)開發(fā)者以正確的方式來書寫代碼漠嵌,避免因?yàn)椴灰?guī)范而導(dǎo)致的問...
    白色鵜鶘鳥閱讀 1,010評論 0 4
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容,還有我對于 Vue 1.0 印象不深的內(nèi)容盖呼。關(guān)于...
    云之外閱讀 5,050評論 0 29
  • a.優(yōu)先級A -- 私有屬性名 在插件儒鹿、混入等擴(kuò)展中始終為自定義的私有屬性使用 $_ 前綴。并附帶一個命名空間以回...
    Ivy_study閱讀 352評論 0 0
  • 組件名為多個單詞必要組件名應(yīng)該始終是多個單詞的几晤,根組件 App 除外约炎。 組件數(shù)據(jù)必要組件的 data必須是一個函數(shù)...
    梁小七閱讀 248評論 0 0
  • 幾個小時前,還是風(fēng)(diao)流(si)倜(nan)儻(shi)的關(guān)先生蟹瘾,還對我侃侃而談圾浅,在夜市告訴我這個可以吃,...