# 前言
开发中,良好的项目风格规范,不仅能对项目起到优化的作用, 还能让我们更快的读懂项目。
# 目录
(一) 组件
(二) prop属性
(三) 样式
(四) 关于key
(五) 模板
(六) 计算属性computed
(七) 指令
(八) 私有属性名
(九) 带引号的特性值
(十) 组件/实例
(十一) 元素特性的顺序
(十二) 非Flux的全局状态管理
# (一) 组件
# 1.命名规范
一个良好的命名规范能够在绝大多数中改善可读性和开发体验
解决方案
- 方案一 : 单文件组件的文件的大小写
单文件组建的文件名始终是首字母大写 (PascalCase),或始终是横线链接 (kebab-case)
components
=> my-components.vue
=> MyComponents.vue
2
3
- 方案二 : 基础组件名
应用特定样式和约定的组件(展示类,无逻辑无状态的组件)应该全部以一个特定的前缀开头,比附 Base, App 或 V,优点如下:
当以私募顺序排列时,应用的基础组件会全部列在一起,容易识别,因为是多个单词,可以避免包裹简单的组件时随意选择前缀(比如 Mybutton,VueButton)
components
=>BaseButton.vue
=>BaseIcon.vue
=>BaseSearch.vue
2
3
4
- 方案三: 单例组件名
只拥有单个活跃实例的组件以 The 前缀命名,以示其唯一性,但这并不是因为这组件只可只用于一个单页面,而是每个页面只用一次,这些组件永远不接受任何 prop ,因为他们是为你的应用定制的,而不是应用的上下文
components
=>TheHeading.vue
=>TheSidebar.vue
2
3
- 方案四: 紧密耦合的组件名
以父组件紧密耦合的子组件应该以父组件作为前缀命名
components
=>TodoList.vue
=>TodoListItem.vue
=>TodoLiisItemButton
2
3
4
- 方案五: 组件名中的单词顺序
组件名应该以更高级别的(通常是一般化的描述)单词开头,以描述新的修饰符结尾
>
compenents
=>SearchButtonClear.vue
=>SearchButtonRun.vue
=>SearchInputQuery.vue
2
3
4
- 方案六 : 完整单词的组件名
组件名应该倾向于完整单词而不是缩写
components
=>UserProfileOptions.vue
2
- 方案七 : 组件为多个单词
组件名应该始终有多个单词组成,根组件 App 除外.这样做可以避免与现有的以及未来的 HTML 冲突
Vue.component('todo-item',{
})
2
3
- 方案八: 模板中的组件名大小写
单文件组件和字符串模板的组件名应该总是单词首字母大写
但是在 DOM 模板中总是横线连接的
<template>
<my-component></my-component>
<MyComponent></MyComponent>
</template>
2
3
4
- 方案九: 自闭合组件
在单文件组件,字符串模板和 JSX 中没有内容的组件应该是自闭合的,但是在 DOM 模板中永远不要这样做。
js/jsx中的组件名应该始终都是PascalCase的,尽管在较为简单的应用中只使用Vue.component进行全局组件注册时,可以使用kebab-case字符串。
<template>
<!--在单文件组件,字符串模板,JSX中-->
<MyComponent />
<!--在DOM模板中-->
<my-component></my-component>
</template>
2
3
4
5
6
7
# 2.组件里的多个特性元素的撰写格式
多个特性元素应该分多行撰写,每个特性一行. 方便易读
<template>
<MyComponent
foo="1"
bar="2"
seo="3">
</MyComponent>
</template>
2
3
4
5
6
7
# 3.组件的data
必须是一个函数
当在组件中使用 data 属性的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数。
当 data 的值是一个对象时,它会在这个组件的所有实例之间共享。想象一下,假如一个 TodoList 组件的数据是这样的:
<script>
export default{
data: {
listTitle: '',
todos: []
}
}
</script>
2
3
4
5
6
7
8
当我们重复使用这个组件的时候,就会产生问题。因为每个组件的实例都引用了相同的数据对象,更改其中一个列表的标题就会改变其它每一个列表的标题。
而这与我们预期的实现效果并不同。我们希望的是每个组件实例都管理自己的数据,因此:每个实例必须生成一个独立的数据对象。在data函数中返回这个对象即可。
<script>
export default{
data: function(){
reuturn {
listTitle: '',
todos: []
}
}
}
</script>
2
3
4
5
6
7
8
9
10
<script>
export default{
data() {
reuturn {
listTitle: '',
todos: []
}
}
}
</script>
2
3
4
5
6
7
8
9
10
//在一个vue的根实例上直接使用对象是可以的
//因为只存在一个这样的实例
new Vue({
data: {
listTitle: '',
todos: []
}
})
2
3
4
5
6
7
8
# (二) prop属性
# 1.命名规范
在声明 prop 的时候,其命名应该始终使用驼峰式命名规则,而在 JSX 中应该使用使用横线连接的方式
<template>
<MyComponent getting-text="haha"> </MyComponent>
</template>
<script>
export default {
props:{
gettingText:String
}
}
</script>
2
3
4
5
6
7
8
9
10
# 2.定义应该尽量详细
在你提交的代码中,prop 的定义应该尽量详细,至少需要指定其类型。
细致的prop 定义有两个好处:
(1) 它们写明了组件的 API,所以很容易看懂组件的用法;
(2) 在开发环境下,如果向一个组件提供格式不正确的 prop,Vue 将会告警,以帮助你捕获潜在的错误来源。
<script>
export default {
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
'syncing',
'synced',
'version-conflict',
'error'
].indexOf(value) !== -1
}
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# (三) 样式
# 1.为组件样式设置作用域
通过 scoped 特性来设置组件样式作用域
<template>
<style scpoed>
/* 样式代码 只在当前组件内生效 */
</style>
</template>
2
3
4
5
# 2.避免在scoped中使用元素选择器
大量的元素选择器比如(button[data-v-fsdfae4]) 会比类和特性组合的选择器慢
<template>
<button class="btn btn-close">X</button>
</template>
<style scoped>
.btn-close {
background-color: red;
}
</style>
2
3
4
5
6
7
8
9
# (四) 关于key
# 1.为列表渲染设置属性 key
key 这个特殊属性主要用在 Vue 的虚拟 DOM 算法中,在对比新旧虚拟节点时辨识虚拟节点。
如果在查找的过程中设置了属性 key , 那么查找速度会快很多。强烈建议在使用 v-for 时提供 key ,毕竟不加会输出警告。
<template>
<div v-for="item in items" :key="item.id" >
<!--内容-->
<div/>
</template>
2
3
4
5
# 2.在v-if/v-if-else/v-else 中使用key
如果一组v-if+v-else的元素类型相同,最好使用属性key(比如两个元素)。
如果添加了属性key,那么在对比虚拟DOM时,则会认为它们是两个同的节点,于是会将旧元素移除并在相同的位置添加一个新元素,从而避免意料之外的副作用。
<template>
<div v-if="error" key="search-status"></div>
<div v-else key="search-status"></div>
</template>
2
3
4
# (五) 模板
# 1.模板中简单的表达式
模板中应该只包含简单的表达式,复杂的表达式应该重构为计算属性或方法
<template>
<div>{{normaliFullName}}</div>
</template>
<script>
export default{
computed:{
normaliFullName:function(){
return this.fullName.map(name=>{
return name[0].toUpperCase() + name.slice(1)
}).join(' ')
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
# (六) 计算属性computed
应该把复杂的计算属性分割为尽可能更多跟简单的属性。
<script>
export default{
computed:{
basePrice:function(){
return this.manufacureCost / ( 1- this.profitMargin)
},
discount:function(){
return this.manufacureCost * ( this.discountPercent || 0)
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
# (七) 指令
# 1.指令缩写
指令缩写 ( 用 : 表示 v-bind: , @ 表示 v-on: ) 保持统一,要么都用,要么都不用
<template>
<input type="text"
v-bind:value="name"
v-on:focus="onFocus"
/>
<input type="text"
:value="name"
@focus="onFocus"
/>
</template>
2
3
4
5
6
7
8
9
10
11
# 避免v-if和v-for一起使用
当处理指令时,v-for 比 v-if 具有更高的优先级,所以即使我们只渲染出列表中的一小部分,也得在每次重渲染的时候遍历整个列表。
解决方案
- 方案一: 使用计算属性过滤
<template>
<div v-for="item in activeUser" :key="item.id" >
<!--内容-->
<div/>
</template>
<script>
computed:{
activeUser:function(){
return this.users.filter(user=>{
return user.isActive
})
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
- 方案二: 循环外层使用v-if
<template>
<div v-if="status">
<div v-for="item in items"></div>
</div>
</template>
2
3
4
5
# 没有在v-if
/v-if-else
/v-else
中使用key
如果一组 v-if + v-else 的元素类型相同,最好使用 key (比如两个 div 元素)。
默认情况下,Vue 会尽可能高效的更新 DOM。这意味着其在相同类型的元素之间切换时,会修补已存在的元素,而不是将旧的元素移除然后在同一位置添加一个新元素。如果本不相同的元素被识别为相同,则会出现意料之外的副作用。
<template>
<div
v-if="error"
key="search-status"
>
错误:{{ error }}
</div>
<div
v-else
key="search-results"
>
{{ results }}
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<p v-if="error">
错误:{{ error }}
</p>
<div v-else>
{{ results }}
</div>
</template>
2
3
4
5
6
7
8
# (八) 私有属性名
在插件、混入等扩展中始终为自定义的私有属性使用 $_ 前缀。并附带一个命名空间以回避和其它作者的冲突 (比如 $_yourPluginName_)。
Vue 使用 _ 前缀来定义其自身的私有属性,所以使用相同的前缀 (比如 _update) 有覆写实例属性的风险。即便你检查确认 Vue 当前版本没有用到这个属性名,也不能保证和将来的版本没有冲突。
对于 $ 前缀来说,其在 Vue 生态系统中的目的是暴露给用户的一个特殊的实例属性,所以把它用于私有属性并不合适。
不过,我们推荐把这两个前缀结合为 $_,作为一个用户定义的私有属性的约定,以确保不会和 Vue 自身相冲突。
var myGreatMixin = {
// ...
methods: {
$_myGreatMixin_update: function () {
// ...
}
}
}
2
3
4
5
6
7
8
# (九) 带引号的特性值
非空 HTML 特性值应该始终带引号 (单引号或双引号,选你 JS 里不用的那个)
在 HTML 中不带空格的特性值是可以没有引号的,但这样做常常导致带空格的特征值被回避,导致其可读性变差
<template>
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
<input type="text">
</template>
2
3
4
# (十) 组件/实例
# 1.选项的顺序
# (1)副作用 (触发组件外的影响)
el
# (2)全局感知 (要求组件以外的知识)
name
parent
# (3)组件类型 (更改组件的类型)
functional
# (4)模板修改器 (改变模板的编译方式)
delimiters
comments
# (5)模板依赖 (模板内使用的资源)
components
directives
filters
# (6)组合 (向选项里合并属性)
extends
mixins
# (7)接口 (组件的接口)
inheritAttrs
model
props
/propsData
# (8)本地状态 (本地的响应式属性)
data
computed
# (9)事件 (通过响应式事件触发的回调)
watch
- 生命周期钩子(按照它们被调用的顺序)
# (10)非响应式的属性 (不依赖响应系统的实例属性)
methods
# (11)渲染 (组件输出的声明式描述)
template
/render
renderError
# (十一) 元素特性的顺序
# 1.定义 (提供组件的选项)
is
# 2.列表渲染 (创建多个变化的相同元素)
v-for
# 3.条件渲染 (元素是否渲染/显示)
v-if
v-else-if
v-else
v-show
v-cloak
# 4.渲染方式 (改变元素的渲染方式)
v-pre
v-once
# 5.全局感知 (需要超越组件的知识)
id
# 6.唯一的特性 (需要唯一值的特性)
ref
key
slot
# 7.双向绑定 (把绑定和事件结合起来)
v-model
# 8.其它特性 (所有普通的绑定或未绑定的特性)
# 9.事件 (组件事件监听器)
v-on
# 10.内容 (覆写元素的内容)
v-html
v-text
# (十二) 非Flux的全局状态管理
应该优先通过 Vuex 管理全局状态,而不是通过 this.$root 或一个全局事件总线。
<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: mapActions(['removeTodo'])
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22