跳轉到內容

優先順序B規則:強烈推薦

這些規則在大多數專案中已被發現可以提高可讀性和/或開發者體驗。即使違反了這些規則,程式碼仍然可以執行,但違規應該很少並且有充分的理由。

元件檔案

每當有可用的構建系統來連線檔案時,每個元件都應該在其自己的檔案中。

這有助於您在需要編輯元件或檢視其使用方法時更快地找到元件。

js
app.component('TodoList', {
  // ...
})

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

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

單檔案元件檔名的大小寫

單檔案元件的檔名應始終使用 PascalCase 或始終使用 kebab-case。

PascalCase 與程式碼編輯器中的自動完成功能配合得最好,因為它與我們如何在 JS(X) 和模板中引用元件一致,儘可能的情況下。然而,有時混合大小寫的檔名可能會在不區分大小寫的檔案系統上建立問題,這就是為什麼 kebab-case 也完全可接受。

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

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

基本元件名稱

應用特定樣式和約定的基本元件(也稱為展示性、啞或純元件)都應該以特定的字首開頭,例如 BaseAppV

詳細說明

這些元件為您應用程式中一致的風格和行為奠定了基礎。它們可能 包含

  • HTML 元素,
  • 其他基本元件,和
  • 第三方 UI 元件。

但它們 永遠不會 包含全域性狀態(例如來自 Pinia 商店)。

它們的名稱通常包含它們所包含的元素的名稱(例如 BaseButtonBaseTable),除非不存在特定目的的元素(例如 BaseIcon)。如果您為更具體的環境構建類似的元件,它們幾乎總是使用這些元件(例如 BaseButton 可能在 ButtonSubmit 中使用)。

此約定的優點

  • 當在編輯器中以字母順序組織時,您應用程式的基本元件都列在一起,這使得它們更容易識別。

  • 由於元件名稱應該是多詞的,因此此約定防止您在選擇簡單的元件包裝器時選擇任意字首(例如 MyButtonVueButton)。

  • 由於這些元件被頻繁使用,您可能希望簡單地使它們全域性,而不是在所有地方匯入。字首使 Webpack 能夠實現這一點

    js
    const requireComponent = require.context(
      './src',
      true,
      /Base[A-Z]\w+\.(vue|js)$/
    )
    requireComponent.keys().forEach(function (fileName) {
      let baseComponentConfig = requireComponent(fileName)
      baseComponentConfig =
        baseComponentConfig.default || baseComponentConfig
      const baseComponentName =
        baseComponentConfig.name ||
        fileName.replace(/^.+\//, '').replace(/\.\w+$/, '')
      app.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

緊密耦合的元件名稱

與父元件緊密耦合的子元件應包含父元件名稱作為字首。

如果一個元件僅在單個父元件的上下文中才有意義,那麼這種關係應該在其名稱中體現出來。由於編輯器通常按字母順序組織檔案,這也使這些相關檔案彼此相鄰。

詳細說明

您可能會被誘惑透過在以父元件命名的目錄中巢狀子元件來解決此問題。例如

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

元件名稱中的單詞順序

元件名稱應從最高階(通常是通用性最強的)詞語開始,並以描述性修飾詞語結束。

詳細說明

您可能會想

"為什麼我們要強迫元件名稱使用不太自然的語言?"

在自然英語中,形容詞和其他描述詞通常出現在名詞之前,而例外需要連線詞。例如

  • 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

由於編輯器通常按字母順序組織檔案,因此現在可以一目瞭然地看到元件之間的重要關係。

您可能會想用不同的方法解決這個問題,將所有搜尋元件巢狀在“搜尋”目錄下,然後將所有設定元件巢狀在“設定”目錄下。我們只建議在非常大的應用程式(例如100+元件)中考慮這種方法,原因如下

  • 與在單個components目錄中滾動相比,導航巢狀子目錄通常需要更多時間。
  • 名稱衝突(例如多個ButtonDelete.vue元件)使得在程式碼編輯器中快速導航到特定元件變得更加困難。
  • 重構變得更加困難,因為查詢和替換通常不足以更新對移動元件的相對引用。

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

自閉合元件

Single-File Components、字串模板和JSX中,沒有內容的元件應該是自閉合的——但永遠不會在DOM模板中使用。

自閉合的元件不僅表明它們沒有內容,而且有意沒有內容。這與一本書中的空白頁和標記為“此頁有意留白”的空白頁之間的區別一樣。您的程式碼在沒有必要關閉標籤的情況下也更乾淨。

不幸的是,HTML不允許自定義元素自閉合——只有官方的“void”元素。這就是為什麼只有在Vue的模板編譯器能夠在DOM之前到達模板時,這種方法才可行,然後提供符合DOM規範的HTML。

template
<!-- In Single-File Components, string templates, and JSX -->
<MyComponent></MyComponent>
template
<!-- In in-DOM templates -->
<my-component/>

template
<!-- In Single-File Components, string templates, and JSX -->
<MyComponent/>
template
<!-- In in-DOM templates -->
<my-component></my-component>

模板中的元件名稱大小寫

在大多數專案中,元件名稱應始終在Single-File Components和字串模板中使用PascalCase - 但在DOM模板中使用kebab-case。

PascalCase比kebab-case有一些優點

  • 編輯器可以在模板中自動完成元件名稱,因為PascalCase也用於JavaScript。
  • <MyComponent>與單詞HTML元素相比,更具有視覺上的區分度,因為有兩個字元差異(兩個大寫字母),而不是隻有一個(一個連字元)。
  • 如果您在模板中使用任何非Vue自定義元素,例如一個Web元件,PascalCase可以確保您的Vue元件保持明顯可見。

不幸的是,由於HTML的大小寫不敏感,DOM模板仍然必須使用kebab-case。

此外,請注意,如果您已經大量投資於kebab-case,與HTML約定的一致性以及能夠在所有專案中使用相同的命名方式可能比上述優點更重要。在這些情況下,在所有地方使用kebab-case也是可接受的。

template
<!-- In Single-File Components and string templates -->
<mycomponent/>
template
<!-- In Single-File Components and string templates -->
<myComponent/>
template
<!-- In in-DOM templates -->
<MyComponent></MyComponent>

template
<!-- In Single-File Components and string templates -->
<MyComponent/>
template
<!-- In in-DOM templates -->
<my-component></my-component>

OR

template
<!-- Everywhere -->
<my-component></my-component>

JS/JSX中的元件名稱大小寫

JS/JSX中的元件名稱應始終為PascalCase,儘管在簡單的應用程式中,在字串中可能使用kebab-case,這些應用程式僅透過app.component進行全域性元件註冊。

詳細說明

在JavaScript中,PascalCase是類和原型建構函式的約定——本質上,任何可以具有獨特例項的東西。Vue元件也有例項,所以使用PascalCase也是合理的。作為額外的優勢,在JSX(和模板)中使用PascalCase允許程式碼的讀者更容易地區分元件和HTML元素。

然而,對於僅透過app.component使用全域性元件定義的應用程式,我們建議使用短橫線命名法。原因如下:

  • 全域性元件在JavaScript中很少被引用,所以遵循JavaScript的命名約定意義不大。
  • 這些應用程式通常包含許多DOM模板,在這些模板中必須使用短橫線命名法。

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

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

完整單片語件名

元件名應優先使用完整單詞而非縮寫。

編輯器中的自動完成功能使編寫較長的名稱的成本非常低,而這些名稱提供的清晰度是無價的。特別是,應始終避免不常見的縮寫。

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

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

屬性名命名規範

屬性名在宣告時始終應使用駝峰命名法。在DOM模板內部使用時,屬性應使用短橫線命名法。單檔案元件模板和JSX可以使用短橫線命名法或駝峰命名法的屬性。命名法應保持一致 - 如果您選擇使用駝峰命名法的屬性,請確保在您的應用程式中不要使用短橫線命名法的屬性。

js
props: {
  'greeting-text': String
}
js
const props = defineProps({
  'greeting-text': String
})
template
// for in-DOM templates
<welcome-message greetingText="hi"></welcome-message>

js
props: {
  greetingText: String
}
js
const props = defineProps({
  greetingText: String
})
template
// for SFC - please make sure your casing is consistent throughout the project
// you can use either convention but we don't recommend mixing two different casing styles
<WelcomeMessage greeting-text="hi"/>
// or
<WelcomeMessage greetingText="hi"/>
template
// for in-DOM templates
<welcome-message greeting-text="hi"></welcome-message>

多屬性元素

具有多個屬性的元素應跨多行,每行一個屬性。

在JavaScript中,將具有多個屬性的物件拆分到多行是一種廣泛認可的約定,因為它更容易閱讀。我們的模板和JSX也應受到同樣的重視。

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

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

模板中的簡單表示式

元件模板應僅包含簡單表示式,將更復雜的表示式重構為計算屬性或方法。

模板中的複雜表示式使它們不那麼宣告化。我們應該努力描述應該出現的內容,而不是我們如何計算該值。計算屬性和方法還允許程式碼重用。

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

template
<!-- In a template -->
{{ normalizedFullName }}
js
// The complex expression has been moved to a computed property
computed: {
  normalizedFullName() {
    return this.fullName.split(' ')
      .map(word => word[0].toUpperCase() + word.slice(1))
      .join(' ')
  }
}
js
// The complex expression has been moved to a computed property
const normalizedFullName = computed(() =>
  fullName.value
    .split(' ')
    .map((word) => word[0].toUpperCase() + word.slice(1))
    .join(' ')
)

簡單的計算屬性

複雜的計算屬性應儘可能拆分為儘可能多的簡單屬性。

詳細說明

簡單、命名良好的計算屬性是:

  • 更容易測試

    當每個計算屬性只包含一個非常簡單的表示式,並且依賴項非常少時,編寫測試來確認它是否正確工作會容易得多。

  • 更容易閱讀

    簡化計算屬性迫使您給每個值賦予一個描述性的名稱,即使它不被重用。這使得其他開發人員(以及未來的您)更容易專注於他們關心的程式碼,並弄清楚發生了什麼。

  • 更適應變化的需求

    任何可以命名的值可能對檢視都有用。例如,我們可能決定顯示一條訊息,告訴使用者他們節省了多少錢。我們可能還決定計算銷售稅,但可能將其單獨顯示,而不是作為最終價格的一部分。

    小型、專注的計算屬性對資訊如何使用的假設更少,因此當需求變化時需要較少的重構。

js
computed: {
  price() {
    const basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice -
      basePrice * (this.discountPercent || 0)
    )
  }
}
js
const price = computed(() => {
  const basePrice = manufactureCost.value / (1 - profitMargin.value)
  return basePrice - basePrice * (discountPercent.value || 0)
})

js
computed: {
  basePrice() {
    return this.manufactureCost / (1 - this.profitMargin)
  },

  discount() {
    return this.basePrice * (this.discountPercent || 0)
  },

  finalPrice() {
    return this.basePrice - this.discount
  }
}
js
const basePrice = computed(
  () => manufactureCost.value / (1 - profitMargin.value)
)

const discount = computed(
  () => basePrice.value * (discountPercent.value || 0)
)

const finalPrice = computed(() => basePrice.value - discount.value)

引號屬性值

非空HTML屬性值應始終在引號內(單引號或雙引號,根據JavaScript中未使用的引號型別而定)。

雖然沒有空格的屬性值在HTML中不需要引號,但這種做法通常會導致避免空格,使得屬性值不那麼易於閱讀。

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

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

指令簡寫

指令簡寫(: 代表 v-bind:@ 代表 v-on:# 代表 v-slot:)應始終使用或從不使用。

template
<input
  v-bind:value="newTodoText"
  :placeholder="newTodoInstructions"
>
template
<input
  v-on:input="onInput"
  @focus="onFocus"
>
template
<template v-slot:header>
  <h1>Here might be a page title</h1>
</template>

<template #footer>
  <p>Here's some contact info</p>
</template>

template
<input
  :value="newTodoText"
  :placeholder="newTodoInstructions"
>
template
<input
  v-bind:value="newTodoText"
  v-bind:placeholder="newTodoInstructions"
>
template
<input
  @input="onInput"
  @focus="onFocus"
>
template
<input
  v-on:input="onInput"
  v-on:focus="onFocus"
>
template
<template v-slot:header>
  <h1>Here might be a page title</h1>
</template>

<template v-slot:footer>
  <p>Here's some contact info</p>
</template>
template
<template #header>
  <h1>Here might be a page title</h1>
</template>

<template #footer>
  <p>Here's some contact info</p>
</template>
優先順序 B 規則:強烈推薦規則已載入