Vue代码规范 -- 强烈推荐篇[译]

优先级B:强烈推荐(提高可读性)

组件文件

只要有能够拼接文件的构建系统,每一个组件应该在它自己的文件里。

当你需要编辑或查看怎样使用它时能够帮助你快速的发现组件。

Bad

1
2
3
4
5
6
Vue.component('TodoList', {
// ...
})
Vue.component('TodoItem', {
// ...
})


Good

1
2
3
components/
| - TodoList.js
| - TodoItem.js

1
2
| - TodoList.vue
| - TodoItem.vue

单文件组件文件名大小写

单文件组件的文件名应该总是使用PascalCase(首字母大写的驼峰式)或总是使用kebab-case(下划线连接式)的写法。
PascalCase对于代码编辑器的自动完成最为友好。因为它和我们在JS(X)和模板中引用组件尽可能是一致的。然而,混用文件名可能导致在大小写不敏感的文件系统中出问题,这就是为什么kebab-case也完全是可以接收的。

Bad

1
2
components/
| - mycomponent.vue

1
2
components/
| - myComponent.vue

Good

1
2
3
components/
| - MyComponent.vue
| - my-component.vue

基本的组件名

基本组件(也称表现的,非智能的或纯组件)应用app的特定样式和惯例应该总是有一个特定的前缀,例如:BaseApp,或V

详解解释
这些组件为应用程序中的一致样式和行为奠定了基础。他们仅仅包含:

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

但是他们绝对不会包含全局状态(eg:来自Vuex商店)。

这些名称常常包含了他们封装的元素名(例如:BaseButtonBaseTable),除非为了某个特定的目的没有元素的存在(例如:BaseIcon)。如果你为更多的具体上下文构建了类似的组件,他们几乎总成充满这些组件(例如:BaseButton可能被用在ButtonSubmit)。
这样做的几点好处:

  • 当编辑器按字母顺序组织排版时,你的应用的基本组件总是被列在一起,使他们更容易识别。
  • 用于组件名称应由多个词构成,这个用法避免你去随意选择前缀来封装你的组件(e.g. Mybutton,VueButton)。
  • 用于这些组件也时常被使用,你可能想要把他们设置成全局属性而避免每次使用他们。使用相同的前缀可以让Webpack这样工作:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    	var requireComponent = require.context("./src", true, /^Base[A-Z]/)
    requireComonent.keys().forEach( function (fileName) {
    var baseComponentConfig = requireComponent(fileName)
    baseComponentConfig = baseComponentConfig.default || baseComponentConfig
    var baseComponentName = baseComponentConfig.name || (
    fileName
    .replace(/^.+\//, '')
    .replace(/\.\w+$/, '')
    )
    Vue.component(baseComponentName, baseComponentConfig)
    })
    >

Bad

1
2
3
4
components/
| - MyButton.vue
| - VueTable.vue
| - Icon.vue


Good

1
2
3
4
components/
| - BaseButton.vue
| - BaseTable.vue
| - BaseIcon.vue

1
2
3
4
components/
| - AppButton.vue
| - AppTable.vue
| - AppIcon.vue
1
2
3
4
compoennts/
| - VButton.vue
| - VTable.vue
| - VIcon.vue

单文件实例组件名

组件应该仅仅有一个激活实例组件以The前缀开头,以表示他们仅仅有一个。

这并不表示组件只能被用在一个单独的页面,而是在每页只能用一次。这些组件不接受任何props,因为他们是为你的app定制的,而不是他们在你的应用中的上下文。如果你发现需要添加props。这是一个好兆头,因为那意味着到现在为止它实际上是一个可以复用的组件而不是每页只能用一次。

Bad

1
2
3
components/
| - Heading.vue
| - MySidebar.vue


Good

1
2
3
components/
| - TheHeading.vue
| - TheSidebar.vue

紧密耦合的组件名

子组件是和他们的父组件紧密耦合的,所有应该包含父组件名称作为前缀。
如果一个组件仅仅在一个单独的父组件的上下文是有意义的,那么他们的关系应该体现在他们的名字上。由于编辑器使用按字母顺序组织文件,所有他们也能够在文件中保持这种关系。

详细解释
你可能想要通过在父组件的目录下嵌套子组件的方式解决这个问题,例如:

1
2
3
4
5
6
components\
| - TodoList/
| - Item/
| - index.vue
| - Button.vue
| - index.vue

或:

1
2
3
4
5
6
components/
| - TodoList\
| - Item/
| - Button.vue
| - Item.vue
| - TodoList.vue

不推荐这样做,其结果是:

  • 许多文件使用相似的名字,使在编辑器中快速切换文件变得困难。
  • 多层被嵌套的子目录,在编辑器的侧边栏查看组件会增加查找时间。

Bad

1
2
3
4
components/
| - TodoList.vue
| - TodoItem.vue
| - TodoButton.vue

1
2
3
components/
| - SearchSidebar.vue
| - NavigationForSearchSidebar.vue

Good

1
2
3
4
components/
| - TodoList.vue
| - TodoListItem.vue
| - TodoListItemButton.vue

1
2
3
components/
| - SearchSidebar.vue
| - SearchSidebarNavigation.vue

组件名中的单词顺序

组件名称应用总是以最高等级的(通常是最常用的)单词开始,以描述修改的单词结尾。

详细解释
你可能会惊讶:
为什么我们强制组件名去使用较少的原生语言?
在本土英语里,形容词和其他描述符一般出现在名词之前,有连接词的例外,例如:

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

如果你喜欢的话,可以定义这些包含连接符的词在组件名里,但是顺序仍然是重要的。

也要注意在你的上下文中什么被认为是“最高级的”。例如,想象一个带搜索表单的app,它可能包含组件像这样:

1
2
3
4
5
6
7
components/
| - ClearSearchButton.vue
| - ExcludeFromSearchInput.vue
| - LaunchOnStartupCheckbox.vue
| - RunSearchButton.vue
| - SearchInput.vue
| - TermsCheckbox.vue

你可能注意到了,它查找特定的搜索组件看起来十分的困难。现在让我们重命名组件根据这个规则:

1
2
3
4
5
6
components/
| - SearchButtonClear.vue
| - SearchButtonRun.vue
| - SearchInputExcludeGlob.vue
| - SettingsCheckboxLaunchOnStartup.vue
| - SettingsCheckboxTerms.vue

由于编辑器使用字母组织文件,组件间的重要关系现在非常明显的看到。

你可能想要使用不同的方式解决这个问题,嵌套所有搜索组件在“search“目录,而后所有的设置组件在settings目录。我们只推荐这种实现在非常大的应用里,(比如超过了100个组件),例如:

  • 在子目录下嵌套常常比在一个components目录下滚动要花费更多的时间。
  • 命名冲突(例如:多个ButtonDelete.vue组件)使代码编辑器快速定位到特定组件变的困难。
  • 重构变得困难,因为查找和替换常常不足以更新对一个移动组件的相对引用。

Bad

1
2
3
4
5
6
7
components/
| - ClearSearchButton.vue
| - ExcludeFromSearchInput.vue
| - LanuchOnStartupCheckbox.vue
| - RunSearchButton.vue
| - SearchInput.vue
| - TermsCheckbox.vue


Good

1
2
3
4
5
6
7
components/
| - SearchButtonClear.vue
| - SearchButtonRun.vue
| - SearchInputQuery.vue
| - SearchInputExcludeGlob.vue
| - SettingsCheckboxTerms.vue
| - SettingsCheckboxLaunchOnStartup.vue

自闭合组件

单文件组件(英文)、字符串模板、和JSX(英文)没有内容的组件应该是自闭合的,但是在DOM模板不要这样做。

自闭合组件传递的信息时不仅没有内容,而且注定没有内容。他们和书的空白页是不同的,有一个标签”这页有意留空“你的代码也会清除不需要的闭合标签。

幸运的是,HTML不允许自定义的元素自闭合的-除了官方的“void”元素(英文)。这就是为什么这个策略是可行的,当Vue的模板编译器可以在DOM之前达到模板,然后产出符合DOM规范的HTML。

Bad

1
2
<! -- 在单文件组件中,字符串模板,JSX -->
<MyComponent></myComponent>

1
<my-component/>

Good

1
2
<! --  在单文件组件,字符串模板,JSX -- >
<MyComponent/>

1
2
<! -- 在DOM模板 -->
<my-component></my-component>

在模板中组件名称大小写

在大部分项目里,组件名称应用总是使用大写开头的驼峰写法在单文件组件和字符串模板里,-但在DOM模板中使用kebab-case。

PascalCase比kebab-case有一些优势:

  • 编辑器在模板中自动完成组件名,因为PascalCase被用在JavaScript。
  • <MyComponent><my-component>更直观的区别于HTML元素,因为有两个不同的自身(两个大写字母),而不是只有一个(连字符)。
  • 如果你使用了任何非Vue自定义元素在你的模板中,例如一个web组件,PascalCase能够确保你的Vue组件仍然明显可见的。

不幸的是,由于HTML不区分大小写,DOM模板必须一致使用kebab-case。

也要注意如果你已经是kebab-case的重度用户,为了保证HTML惯例的一致在你所有的项目中使用同样的大小写可能比上面所列出的要更重要,在这种情况下,使用kebab-case在所有地方是可以接受的。

Bad

1
2
<! -- 在单文件组件和字符串模板中 -->
<mycomponent/>

1
2
<! --  在DOM模板 -- >
<MyComponent></MyComponent>

Good

1
2
<! -- 在当文件组件和字符串模板中 -->
<MyComponent/>

1
2
<! -- 在DOM模板 -->
<my-component></my-component>


1
2
<! -- 在任何地方 -->
<my-component></my-component>

在JS/JSX组件名大小写

在JS/JSX中应该总是使用首字母大写的驼峰写法,虽
然他们可能使用连字符kebab-case的写法,但这仅仅用于一些通过Vue.component全局组件注册的简单应用程序中。

详细解释
在JavaScript中,PascalCase(驼峰)的写法对于类和原型构造器是惯例 –基本上,任何东西都拥有实例。Vue组件也是实例,所以它使用PascalCase的写法是有意义的。作为一个额外的好处,在JSX(和模板中)使用PascalCase写法可以使读者更好的区分组件和HTML元素的不同。

然而,对与使用Vue.component定义的全局组件来说,我们推荐使用kebab-case(下划线连接)的写法代替。因为:

  • 全局组件在JavaScript中引用比较少见,所以遵循这个惯例对应JavaScript来说意义不大。
  • 这些应用内总是包含许多内在的DOM模板,使用kebab-case是必须的。

Bad

1
2
3
Vue.component('myComponent', {
// ...
})

1
import myComponent from './MyComponent.vue'
1
2
3
4
export default {
name: 'myComponent',
// ...
}
1
2
3
4
export default {
name: 'my-component',
// ...
}

Good

1
2
3
Vue.component('MyComponent', {
// ...
})

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

完整单词的组件名

组件名称应该尽量用完整的单词而不是缩写
编辑器里的自动完成会使我们写长词花费的时间非常少,而且完整的单词会使释义非常清晰。尤其是不常用的缩写,应该尽量避免。
Bad

1
2
3
components/
|- SdSettings.vue
|- UProfOpts.vue

Good

1
2
3
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

Prop名称的大小写

Prop的名称在声明期间应该总是使用cameCase(首字母小写的驼峰式),但是kebab-case用在模板和JSX

我们需要简单的遵守每一种语言的惯例。在JavaScript中,camelCase更常用,在HTML,是kebab-case。

Bad

1
2
3
4
// In JS
props: {
'greeting-text':String
}

1
2
// In HTML
<WelcomeMesssge greetingText="hi"/>

Good

1
2
3
4
// In JS
props: {
greetingText: String
}

1
2
// In HTML
<WelcomeMessage greeting-text="hi"/>

多个attribute元素

使用多个attributes的元素应该分为多行,每行使用一个attribute。

在JavaScript,用多行切分对象的多个properties被认为是一种好习惯,因为易读。我们的模板和JSX应该使用相同的方式。
Bad

1
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">

1
<MyComponent foo="a" bar="c" baz="c" />

Good

1
2
3
4
<img
src="https://vuejs.org/images/logo.png"
alt="Vue Logo"
>

1
2
3
4
5
<MyComponent
foo="a"
bar="b"
baz="c"
/>

在模板中的简单表达式

组件模板应该仅仅包含简单的表达式,多个复杂的表达式应该使用计算属性或方法重构。

模板中的复杂表达式使他们不那么具有声明性。我们应该集中描述会出现什么,而不是我们怎样计算它的值。计算属性和方法也允许我们代码复用。
Bad

1
2
3
4
5
6
// In HTML
{{
fullName.split(' ').map(function (word){
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}

Good

1
2
3
4
5
6
7
8
9
10
11
12
13
// In HTML
<!-- In a template>
{{ normallizedFullName }}

// In JavaScript
// The complex expression has been moved to a computed properties
computed: {
normalizedFullName: function() {
return this.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}

简单的计算属性

复杂的计算属性应该尽可能的分割为多个简单的计算属性。

详细解释
简单的,命名良好的计算属性是这样的:

  • 容易测试
    当每一个计算属性包含仅仅一个简单表达式的时候,使用比较少的依赖,
    编写测试来确认他们可以正确工作要容易的多。
  • 容易阅读
    特定的计算属性集中于你名字所描述的每一个值,即使他们不可以复用。
    这使得其他开发者(和未来的你)能够更集中于关心和计算你的代码发生
    了什么。
  • 更能适应需求的改变
    被命名的任何值都可能是对视图有用的,例如,我们可能要去显示一条消
    息来告诉用户他们存了多少钱。我们也可能打算去计算销售税,但是可能
    分开显示他们,而不是作为最终价格的一部分。

小的,专注于计算属性使信息怎样被使用的假设条件更少,所以当需求改变时减少重构。

Bad

1
2
3
4
5
6
7
8
9
10
// In JS
computed: {
price: function() {
var basePrice = this.manufactureCost / (1 - this.profitMargin)
return (
basePrice -
basePrice * (this.discountPercent || 0)
)
}
}

Good

1
2
3
4
5
6
7
8
9
10
11
12
// In JS
computed: {
basePrice: function () {
return this.manufactueCost / (1 - this.profitMargin)
},
discount: function () {
return this.basePrice * (this.discountPercent || 0)
},
finalPrice: function() {
return this.basePrice - this.discount
}
}

带引号的特性值

非空HTML特性值应该总是在引号内部(单引号或双引号,在JS里不用的那个)

在HTML中不带空格的特性值是可以没有引号的,但这里鼓励大家不写空格,使特性值的可读性变差。

Bad

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

Good

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

指令缩写

指令缩写(for v-bind:@ for v-on:)应该总是使用或从不使用。

Bad

1
2
3
4
<input
v-bind:value="newTodoText"
:placeholder="newTodoINstructions"
>
1
2
3
4
<input
v-on:input="onInput"
@focus="onFocus"
>

Good

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

原文:Priority B Rules: Strong Recommended(Improving Readalibity)(英)