# 风格指南

# 介绍

在参与H3-UI开发时,请遵守约定的单文件组件风格指南,指南内容节选自 Vue 官方风格指南

# 组件数据

组件的 data 必须是一个函数。

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

// bad
export default {
  data: {
    foo: 'bar'
  }
}

// good
export default {
  data () {
    return {
      foo: 'bar'
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 单文件组件文件名称

基于文件名始终小写的规则,单文件组件的文件名应该始终是横线连接 (kebab-case)。

// bad
mycomponent.vue
myComponent.vue
MyComponent.vue

// good
my-component.vue
1
2
3
4
5
6
7

# 紧密耦合的组件名

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

// bad
components/
|- TodoList.vue
|- TodoItem.vue
└─ TodoButton.vue

// good
components/
|- todo-list.vue
|- todo-list-item.vue
└─ todo-list-item-button.vue
1
2
3
4
5
6
7
8
9
10
11

# 自闭合组件

在单文件组件中没有内容的组件应该是自闭合的。

<!-- bad -->
<my-component></my-component>

<!-- good -->
<my-component />
1
2
3
4
5

# Prop 名大小写

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

// bad
export default {
  props: {
    'greeting-text': String
  }
};

// good
export default {
  props: {
    greetingText: String
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- bad -->
<welcome-message greetingText="hi" />

<!-- good -->
<welcome-message greeting-text="hi" />
1
2
3
4
5

# Props 换行

多个 Props 的元素超过3个属性应该分多行撰写,每个 Props 一行,闭合标签单起一行。

<!-- bad -->
<my-component id="d" foo="a" bar="b" baz="c"  />

<!-- good -->
<my-component
  id="d"
  foo="a"
  bar="b"
  baz="c"
/>
1
2
3
4
5
6
7
8
9
10

# 指令缩写

指令缩写,用 : 表示 v-bind: ,用 @ 表示 v-on:

<!-- bad -->
<input
  v-bind:value="value"
  v-on:input="onInput"
>

<!-- good -->
<input
  :value="value"
  @input="onInput"
>
1
2
3
4
5
6
7
8
9
10
11

# Props 顺序

标签的 Props 应该有统一的顺序,依次为指令、属性和事件。

<my-component
  v-if="if"
  v-show="show"
  v-model="value"
  ref="ref"
  :key="key"
  :text="text"
  @input="onInput"
  @change="onChange"
/>
1
2
3
4
5
6
7
8
9
10

# 组件选项的顺序

组件选项应该有统一的顺序。

export default {
  name: '',

  mixins: [],

  components: {},

  props: {},

  data() {},

  computed: {},

  watch: {},

  created() {},

  mounted() {},

  destroyed() {},

  methods: {}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 组件选项中的空行

组件选项较多时,建议在属性之间添加空行。

export default {
  computed: {
    formattedValue() {
      // ...
    },

    styles() {
      // ...
    }
  },

  methods: {
    onInput() {
      // ...
    },

    onChange() {
      // ...
    }
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#v-for 设置键值

总是用 key 配合 v-for

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

# 详解

假设你有一个待办事项列表:

data: function () {
  return {
    todos: [
      {
        id: 1,
        text: '学习使用 v-for'
      },
      {
        id: 2,
        text: '学习使用 key'
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

然后你把它们按照字母顺序排序。在更新 DOM 的时候,Vue 将会优化渲染把可能的 DOM 变动降到最低。即可能删掉第一个待办事项元素,然后把它重新加回到列表的最末尾。

这里的问题在于,不要删除仍然会留在 DOM 中的元素。比如你想使用 <transition-group> 给列表加过渡动画,或想在被渲染元素是 <input> 时保持聚焦。在这些情况下,为每一个项目添加一个唯一的键值 (比如 :key="todo.id") 将会让 Vue 知道如何使行为更容易预测。

根据我们的经验,最好始终添加一个唯一的键值,以便你和你的团队永远不必担心这些极端情况。也在少数对性能有严格要求的情况下,为了避免对象固化,你可以刻意做一些非常规的处理。

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

<!-- good -->
<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 避免 v-ifv-for 用在一起 必要

永远不要把 v-ifv-for 同时用在同一个元素上。

一般我们在两种常见的情况下会倾向于这样做:

  • 为了过滤一个列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。

  • 为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ul, ol)。

# 详解

当 Vue 处理指令时,v-forv-if 具有更高的优先级,所以这个模板:

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8
9

相当于进行了以下的运算:

this.users.map(function (user) {
  if (user.isActive) {
    return user.name
  }
})
1
2
3
4
5

因此即使我们只想渲染出活跃的用户(user.isActive)的元素,也得在每次重渲染的时候遍历整个列表。

通过将其更换为在如下的一个计算属性上遍历:

computed: {
  activeUsers: function () {
    return this.users.filter(function (user) {
      return user.isActive
    })
  }
}
1
2
3
4
5
6
7
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8

我们将会获得如下好处:

  • 过滤后的列表会在 users 数组发生相关变化时才被重新运算,过滤更高效。
  • 使用 v-for="user in activeUsers" 之后,我们在渲染的时候遍历活跃用户(user.isActive),渲染更高效。
  • 解耦渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。

另一种情况下,v-if的判断条件不是元素内的属性

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

<!-- good -->
<ul v-if="shouldShowUsers">
  <li
    v-for="user in users"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

通过将 v-if 移动到容器元素,我们不会再对列表中的每个用户检查 shouldShowUsers。取而代之的是,我们只检查它一次,且不会在 shouldShowUsers 为否的时候运算 v-for

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8
<ul v-if="shouldShowUsers">
  <li
    v-for="user in users"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8

# 单文件组件顶级标签的顺序

单文件组件应该总是让顶级标签的顺序保持一致,且标签之间留有空行。

<template>
...
</template>

<script>
/* ... */
</script>

<style>
/* ... */
</style>
1
2
3
4
5
6
7
8
9
10
11