在大多数项目中,这些规则被发现可以提高可读性和/或开发人员体验。如果你违反了它们,你的代码仍然会运行,但违规应该是罕见的,而且有充分的理由。
每当构建系统可用于连接文件时,每个组件都应位于其自己的文件中。
这有助于您在需要编辑组件或查看如何使用组件时更快地找到组件。
//坏
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
应用特定于应用的样式和约定的基本组件(也称为表示性组件、哑组件或纯组件)都应以特定前缀开头,例如 Base、 App或 V。
这些组件为应用程序中的一致样式和行为奠定了基础。它们可能仅包含:加粗样式
- HTML 元素,
- 其他基本组件,
- 第三方 UI 组件
但它们永远不会包含全局状态(例如,来自 Pinia 存储)。
它们的名称通常包括它们包装的元素的名称(例如 BaseButton、 BaseTable),除非不存在用于其特定目的的元素(例如 BaseIcon)。如果您为更具体的上下文构建类似的组件,它们几乎总是会使用这些组件(例如 BaseButton 可用于 ButtonSubmit)。
该公约的一些优点:
- 在编辑器中按字母顺序排列时,应用的基本组件会全部列出在一起,从而更易于识别。
- 由于组件名称应始终为多字,因此此约定可防止您为简单的组件包装器选择任意前缀(例如 MyButton、 VueButton)。
- 由于这些组件的使用频率非常高,因此您可能希望简单地将它们设置为全局组件,而不是将它们导入到任何地方。前缀使 Webpack 成为可能。
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
不建议这样做,因为它会导致:
1.许多文件具有相似的名称,这使得代码编辑器中的快速文件切换更加困难。
2.许多嵌套的子目录,这增加了在编辑器侧边栏中浏览组件所需的时间。
//坏
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
//或者
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
//好
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
//或者
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
组件名称应以最高级别(通常是最通用)的单词开头,并以描述性修饰单词结尾。
比如我们要形容一个好看的女孩子,在中国一般都是说:很好看的女孩子,修饰的名字放在后面,而用英语来说的话the gird is beautiful,名词在前面,对于组件命名也是这样的。把最重要的名词放在命名前面,可以一目了然。
您可能想知道:
“我们为什么要强制组件名称使用不太自然的语言?”
在自然英语中,形容词和其他描述符通常出现在名词之前,而例外则需要连接词。例如:
- 牛奶咖啡 with
- 的汤 of the 当天
- 参观者 to the 博物馆
如果您愿意,您绝对可以在组件名称中包含这些连接器单词,但顺序仍然很重要。
另请注意, 被视为“最高级别”的内容将与应用的上下文相关 。例如,假设一个带有搜索表单的应用程序。它可能包括如下组件: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
没有内容 的组件应该在单文件组件 、字符串模板和 JSX 中自关闭,但在 DOM 模板中永远不要。
自关闭的组件传达出它们不仅没有内容,而且 意味着 没有内容。这是一本书中的空白页和标有“此页故意留空”的空白页之间的区别。 您的代码也更简洁,没有不必要的结束标记。
不幸的是,HTML 不允许自定义元素是自闭合的——只允许官方的 “void”元素 。这就是为什么只有当 Vue 的模板编译器可以在 DOM 之前到达模板,然后提供符合 DOM 规范的 HTML 时,该策略才有可能。
//坏
<!-- In Single-File Components, string templates, and JSX -->
<MyComponent></MyComponent>
<!-- In in-DOM templates -->
<my-component/>
//好
<!-- In Single-File Components, string templates, and JSX -->
<MyComponent/>
<!-- In in-DOM templates -->
<my-component></my-component>
和字符串模板中应始终为 PascalCase 在大多数项目中,组件名称在单文件组件 ,但在 DOM 模板中应为 kebab-case。
与烤肉串相比,PascalCase 有几个优点:
不幸的是,由于 HTML 不区分大小写,DOM 模板仍然必须使用 kebab-case。
另请注意,如果您已经在 kebab-case 上投入了大量资金,那么与 HTML 约定的一致性以及能够在所有项目中使用相同的大小写可能比上面列出的优势更重要。在这些情况下, 到处使用烤肉串也是可以接受的。
//坏
<!-- In Single-File Components and string templates -->
<mycomponent/>
<!-- In Single-File Components and string templates -->
<myComponent/>
<!-- In in-DOM templates -->
<MyComponent></MyComponent>
//好
<!-- In Single-File Components and string templates -->
<MyComponent/>
<!-- In in-DOM templates -->
<my-component></my-component>
<!-- Everywhere -->
<my-component></my-component>
组件名称应优先使用完整单词而不是缩写。
编辑器中的自动补全功能使编写较长名称的成本非常低,而它们提供的清晰度是无价的。特别是,应始终避免使用不常见的缩写。
//坏
components/
|- SdSettings.vue
|- UProfOpts.vue
//好
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
在声明期间,道具名称应始终使用 camelCase。在 DOM 模板中使用时,props 应该是烤肉串大小写的。单文件组件模板和 JSX 可以使用 kebab-case 或 camelCase 道具。外壳应保持一致 - 如果您选择使用骆驼壳式道具,请确保在应用程序中不使用烤肉串式道具
//坏
const props = defineProps({
'greeting-text': String
})
//坏
// for in-DOM templates
<welcome-message greetingText="hi"></welcome-message>
//好
const props = defineProps({
greetingText: String
})
//好
// 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"/>
//好
// for in-DOM templates
<welcome-message greeting-text="hi"></welcome-message>
组件模板应仅包含简单的表达式,并将更复杂的表达式重构为计算属性或方法。
模板中的复杂表达式会降低其声明性。我们应该努力描述 what 应该出现什么,而不是 我们如何 计算该值。计算的属性和方法还允许重用代码。
//坏
{{
fullName.split(' ').map((word) => {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
//好
<!-- In a template -->
{{ normalizedFullName }}
//好
// 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(' ')
)
复杂的计算属性应拆分为尽可能多的简单属性。
简单来说有三点好处
//坏
const price = computed(() => {
const basePrice = manufactureCost.value / (1 - profitMargin.value)
return basePrice - basePrice * (discountPercent.value || 0)
})
//好
const basePrice = computed(
() => manufactureCost.value / (1 - profitMargin.value)
)
const discount = computed(
() => basePrice.value * (discountPercent.value || 0)
)
const finalPrice = computed(() => basePrice.value - discount.value)