Vue代码规范 -- 推荐 & 谨慎使用篇[译]

组件/实例选项顺序

组件/实例选项的顺序应该一致
这是我们推荐的组件选项的默认顺序。他们被分成不同的分类,所以你会知道在插件的哪里添加新的属性。

  1. 副作用(组件之外触发)
  • el
  1. 全局意识(要求组件之外的知识)
  • name
  • parent
  1. 组件类型(改变组件的类型)
  • functional
  1. 组件修饰符(改变组件被编译的方式)
  • delimiters
  • comments
  1. 组件依赖(资源在模板内使用)
  • components
  • directive
  • filters
  1. Composition(将属性合并到选项内)
  • extends
  • mixins
  1. Interface(组件界面)
  • inheritAttrs
  • model
  • props/propsData
  1. 本地状态(本地响应属性)
  • data
  • computed
  1. Event(响应事件触发回调)
  • watch
  • 事件循环(按它们被调用的顺序)
    • beforeCreate
    • created
    • beforeMount
    • mounted
    • beforeUpdate
    • updated
    • activated
    • deactivated
    • beforeDestory
    • destroyed
  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. Events(组件事件监听)
  • v-on
  1. 内容(复写元素的内容)
  • v-html
  • v-text

在组件/实例选项内空行

你可能想在多行属性之间添加空行,特别是如果这些选项在非滚动的屏幕不合适时。

当组件感觉狭小或阅读困难,在多行属性之间添加空格可以使他们更容易浏览。在一些编辑器,例如Vim,这样的格式化选项也可以使他们更容易在键盘上导航。
Good

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
props: {
value: {
type: String,
required: true
},

focused: {
type: Boolean,
default: false
},

label: String,
icon: String
},

computed: {
formattedValue: function () {
// ...
},

inputClasses: function () {
// ...
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 没有空格也可以只要是组件
// 容易阅读和导航
props: {
value: {
tpye: String,
required: true
},
focused: {
type: Boolean,
default: false
},
label: String,
icon: String
},
computed: {
formattedValue: function () {
// ...
},
inputClasses: function() {
// ...
}
}

单文件组件最外层元素的顺序

单文件组件应该总是<script>,<template>,和<style>标签顺序,<style>在最后,因为另外两个标签至少要有一个。
Bad

1
2
3
4
// In HTML
<style>/* ... */</style>
<script>/* ... */</script>
<template>...</template>

1
2
3
4
5
6
7
8
9
10
// In HTML
<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>

<!-- Component.vue> -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>

Good

1
2
3
4
5
6
7
8
9
10
// In HTML
<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>

1
2
3
4
5
6
7
8
9
<!-- ComponentA.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>

优先级D规则:谨慎使用(有潜在危险的模式)

v-if/v-else-if/v-else没有key

最好使用携带keyv-if + v-else,如果他们是相同而元素类型(e.g.都是<div>元素)。
默认情况下,Vue尽可能高效的更新DOM,那意味着当切换两个相同元素的时候,它仅仅分发已经存在的元素,而不出删除它再去添加一个新的。如果这些元素实际上不应该被认为是同一个可能产生意外的后果
Bad

1
2
3
4
5
6
<div v-if="error">
Error: {{ error }}
</div>
<div v-else>
{{ results }}
</div>

Good

1
2
3
4
5
6
7
8
9
10
11
12
<div
v-if="error"
key="seach-status"
>
Error: {{ error }}
</div>
<div
v-else
key="search-results"
>
{{ results }}
</div>

1
2
3
4
5
6
<p v-if="error">
Error:{{error}}
</p>
<div v-else>
{{ results }}
</div>

使用scoped的元素选择器

元素选择器应该避免在scoped中出现

scoped样式中,应该首选类选择器而不是元素选择器,因为大量使用元素选择器是很慢的。

详细解释
对于scoped样式,Vue为组件元素添加了唯一特性,例如:data-v-f3f3eg9 。然后修改选择器,以便匹配这些与此元素匹配的元素(例如:button[data-v-f3f3eg9]).

问题是大量的元素特性选择器(例如:button[data-v-f3f3eg9])将会相当慢比类特性选择器,所以无论何时类选择器是首选。

Bad

1
2
3
4
5
6
7
8
9
<template>
<button>X</button>
</template>

<style scoped>
button {
background-color: red;
}
</style>

Good

1
2
3
4
5
6
7
8
9
<template>
<button class="btn btn-close">X</button>
</template>

<style scoped>
.btn-close {
background-color: red
}
</style>

隐式的父-子组件通信

Props和事件应该作为父-子间通信的首选,代替this.$parent或改变props。

一个理想的Vue应用是props向下传递,事件向上传递。坚持这个惯例使你的组件更容易理解。然而,在一些边缘情况下prop的变更或this.$parent能够简化两个已经深度耦合的组件。

问题是,在许多简单的场景下这种模式可能提供便利。但请当心:不要为了短期的方便(少写代码)而以能够理解的状态流作为交换。

bad

1
2
3
4
5
6
7
8
9
10
// In JS
Vue.component('TodoItem',{
props: {
todo: {
type: Object,
required: true
}
},
template: '<input v-model="todo.text">'
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Vue.component('TodoItem',{
props: {
todo: {
type: Object,
required: true
}
},
methods: {
removeTodo () {
var vm = this
vm.$parent.todos = vm.$parent.todos.filter(function (todo) {
return todo.id !== vm.todo.id
})
}
},
template: `
<span>
{{ todo.text }}
<button @click="removeTodo">
X
</button>
</span>
`
})

Good

1
2
3
4
5
6
7
8
9
10
11
12
Vue.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: `
<input
:value="todo.text"
@input="$emit('input', $event.target.value)"
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Vue.component('TodoItem',{
props: {
todo: {
type: Object,
required: true
}
},
template: `
<span>
{{todo.text}}
<button @click="$emit('delete')"
X
</button>
</span>
`
})

非Flux状态管理

Vuex是全局状态管理的首选,代替this.$root或一个全局事件总线。

使用this.$root和/或使用全局事件总线管理状态是一种惯例对于非常简单的场景,但是对大多数应用不合适。Vuex不仅提供了一个中心区域去管理状态,而且也是组织、追踪和调试状态改变的工具。

Bad

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// main.js
new Vue({
data: {
todos:[]
},
created: function () {
this.$on('remove-todo',this.removeTodo)
},
methods:{
removeTodo: function (todo) {
var todoIdToRemove = tood.id
this.todos = this.todos.filter(function (todo) {
return todo.id !== todoToRemove
})
}
}
})

Good

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// store/modules/todos.js
export default {
state: {
list: []
},
mutations: {
REMOVE_TODO(state,todoId) {
state.list = state.list.filter(todo => todo.id !== toodId)
}
},
actions: {
removeTodo ({ commit, state }, todo) {
commit('REMOVE_TODD',todo.id)
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// In HTML
<!-- TodoItem.vue -->
<template>
<span>
{{ todo.text }}
<button @click="removeTodo(todo)">
X
</button>
</span>
</template>

<script>
import { mapActions } from 'vuex'

export default {
props: {
todo: {
type: Object,
required: true
}
},
methods: mapAcitons([''removeTod'])
}
</script>

原文:Priority C & D Rules: Recommended and Use with Caution