Skip to content

风格指南

必要的

组件名为多个单词

根组件App除外 这样做可以避免跟现有的以及未来的HTML元素相冲突,因为所有的HTML元素名称都是单个单词的

js
Vue.component('my-compponent', {
  data() {
    return {
      text: ''
    }
  }
})

// .vue
export default {
  data() {
    return {
      text: ''
    }
  }
}

组件数据 data 必须是一个函数

当在组件中使用data属性时,除了new Vue外的任何地方,它的值必须是返回一个对象的函数

Prop 定义应该尽量详细

props: ['status'] ps 这样做只有开发原型系统时可以接受

js
export default {
  props: {
    status: {
      type: String,
      require: true,
      validator(value) {
        return [
          'syncing',
          'synced',
          'version-conflict',
          'error'
        ].indexOf(value) !== -1
      }
    }
  }
}

为v-for设置键值

总是用key配合v-for

在组件上总是必须用key配合v-for,以便维护内部组件及其子树的状态。 甚至在元素上维护可预测的行为,比如动画中的对象固化 object constancy 也是一种好的做法

html
<ul>
  <li v-for="item in list" :key="item.id">
    {{item.text}}
  </li>
</ul>

为组件样式设置作用域

对于应用来说,顶级App组件和布局组件的样式可以是全局的,但是其他所有组件都应该是有作用域的

这条规则只和单文件组件。不管怎样,对于组件库,我们应该更倾向于选用基于class的策略而不是scoped特性

详解:

如果你和其他开发者一起开发一个大型工程,或有时引入三方HTML/CSS,设置一致的作用域会确保你的样式只会运用在它们想要作用的组件上。 不止要使用scoped特性,使用唯一的class名可以帮你确保那些三方库的CSS不会运用在你的HTML上。

html
<!-- 使用scoped特性 -->
<template>
  <button class="button button-close">x</button>
</template>
<style scoped>
.button {}
.button-close {}
</style>
<!-- 使用 CSS Modules -->
<template>
  <button :class="[$style.button, $style.buttonClose]">x</button>
</template>
<style module>
.button {}
.buttonClose {}
</style>
<!-- 使用 BEM 约定 -->
<template>
  <button class="c-Button c-Button--close">x</button>  
</template>
<style>
.c-Button {}
.c-Button--close {}
</style>

私有属性名

在插件、混入等扩展中始终为自定义的私有属性使用 $_前缀,并附带有个命名空间以回避和其他作者的冲突 如$_yourPluginName_

详解:

Vue使用_前缀来定义其自身的私有属性,所以使用相同的前缀 如 _update 有覆盖实例属性的危险。即便你检查确认Vue当前版本没有用到该属性,也不能保证和将来的版本没有冲突。

对于$前缀来说,其在Vue生态系统中的目的是暴露给用户的一个特殊的实例属性,所以把它用于私有属性并不合适。

不过我们推荐把这两个前缀结合为$_,作为一个用户定义的私有属性的约定,以确保不会和Vue自身起冲突

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

优先级B规则: 强烈推荐 增强可读性

组件文件

只要有能够拼接文件的构建系统,就把每个组件单独分成文件

单文件组件文件的大小写

单文件组件的文件名要么始终是单词大写开头 PascalCase,要么始终是横线连接 kebab-case 项目中我们使用的是 kebab-case

基础组件名

应用特定样式和约定的基础组件,应该全部以一个特定的前缀开头 (项目用的是 c-)

详解:

这些组件为应用奠定了一致的基础样式和行为,它们可能只包括:

HTML元素 其他带Base前缀的组件 第三方UI组件库

但是它们绝不会包括全局状态(比如来自Vuex store)

js
base/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
common/
|- CButton.vue
|- CTable.vue
|- CIcon.vue

单例组件名

只应该拥有单个活跃实例的组件 应该以 The 前缀命名,以示其唯一性

这不意味着组件只可用于一个单页面,而是每个页面只使用一次。这些组件永不接受任何prop,因为它们是为你的应用定制的,而不是它们在你的应用中的上下文。 如果你发现有必要添加prop,那就表明这实际上是一个可复用组件,只是目前在每个页面里只使用一次。

紧密耦合的组件名

和父件紧密耦合的子组件应该以父组件名作为前缀命名

如果一个组件只在某个父组件的场景下有意义,这层关系应该体现在其名字上。因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起。

js
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- todo-list.vue
|- todo-list-item.vue
|- todo-list-item-button.vue

组件名中的单词顺序

组件名应该以高级别的单词开头,以描述性的修饰词结尾

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

自闭合组件

在单文件组件、字符串模版和JSX中没有内容的组件是自闭合的——但在DOM模板里永远不要这样做。

html
<!-- 在单文件组件、字符串模版和JSX中 -->
<MyComponent />

<!-- 在DOM模板中 -->
<my-component></my-component>

模板中的组件名大小写

对于绝大多数项目来说,在单文件组件和字符串模版中组件名应该总是ParcalCase的,但在DOM模板中总是kebab-case的

完整单词的组件名

组件名应该倾向于完整单词而不是缩写。

Prop名大小写

在声明 prop 的时候,其命名应该始终使用 camelCase,而在模板和 JSX 中应该始终使用 kebab-case。

js
export default {
  props: {
    greetingText: String
  }
}

多个特性的元素

多个特性的元素应该分多行撰写,每个特性一行。在 JavaScript 中,用多行分隔对象的多个属性是很常见的最佳实践

模版中简单的表达式

组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法

复杂表达式会让你的模板变得不那么声明式,我们应该尽量描述应该出现的是什么,而非如何计算某个值。而且计算属性和方法使得代码可以重用。

js
// 在模板中
{{normalizedFullName}}
// 复杂表达式已移入一个计算属性
computed: {
  normalizedFullName() {
    return this.fullName.split(' ').map(word => {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}

简单的计算属性

应该把复杂计算属性分隔为尽可能多的更简单的属性

更简单、命名得当的计算属性是这样的:

  • 易于测试 当每个计算属性都包含一个非常简单且很少依赖的表达式时,撰写测试以确保其正确工作就会更加容易。

  • 易于阅读 简化计算属性要求你为每一个值都起一个描述性的名称,即便它不可复用。这使得其他开发者 (以及未来的你) 更容易专注在他们关心的代码上并搞清楚发生了什么。

  • 更好的“拥抱变化” 任何能够命名的值都可能用在视图上。举个例子,我们可能打算展示一个信息,告诉用户他们存了多少钱;也可能打算计算税费,但是可能会分开展现,而不是作为总价的一部分。 小的、专注的计算属性减少了信息使用时的假设性限制,所以需求变更时也用不着那么多重构了。

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

带引号的特征值

非空HTML特性值应该始终带引号(单引号或双引号)

指令缩写

: -> v-bind: @ -> v-on:

推荐写法

组件/实例的选项顺序

推荐组件选项的默认顺序

  1. 副作用 触发组件外的影响

el

  1. 全局感知

name parent

  1. 组件类型 更改组件的类型

functional

  1. 模板修改器

delimiters comments

  1. 模板依赖

components directives filters

  1. 组合

extends mixins

  1. 接口 组件的接口

inheritAttrs model props / propsData

  1. 本地状态 本地的响应式属性

data computed

  1. 事件 通过响应式事件触发的回调

watch 声明周期钩子,按照它们被调用的顺序

  1. 非响应式的属性 (不依赖响应式系统的实例属性)

methods

  1. 渲染 组件输出的声明式描述

template / render renderError

元素特性的顺序

  1. 定义 提供组件

is

  1. 列表渲染 创建多个变化的相同元素

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. 双向绑定

v-model

  1. 其他特性

  2. 事件

v-on

  1. 内容

v-html v-text

组件/实例选项中的空行

单文件组件的顶级元素顺序

template script style

隐性的父子组件通信

应该优先通过 prop 和事件进行父子组件之间的通信,而不是 this.$parent 或改变 prop。

共 20 个模块,1301 篇 Markdown 文档。