# 前言

开发中,良好的项目风格规范,不仅能对项目起到优化的作用, 还能让我们更快的读懂项目。

# 目录

(一) 组件
(二) prop属性
(三) 样式
(四) 关于key
(五) 模板
(六) 计算属性computed
(七) 指令
(八) 私有属性名
(九) 带引号的特性值
(十) 组件/实例
(十一) 元素特性的顺序
(十二) 非Flux的全局状态管理

# (一) 组件

# 1.命名规范

一个良好的命名规范能够在绝大多数中改善可读性和开发体验

解决方案

  • 方案一 : 单文件组件的文件的大小写

    单文件组建的文件名始终是首字母大写 (PascalCase),或始终是横线链接 (kebab-case)

components
 => my-components.vue
 => MyComponents.vue
1
2
3
  • 方案二 : 基础组件名

    应用特定样式和约定的组件(展示类,无逻辑无状态的组件)应该全部以一个特定的前缀开头,比附 Base, App 或 V,优点如下:
    当以私募顺序排列时,应用的基础组件会全部列在一起,容易识别,因为是多个单词,可以避免包裹简单的组件时随意选择前缀(比如 Mybutton,VueButton)

components
=>BaseButton.vue
=>BaseIcon.vue
=>BaseSearch.vue
1
2
3
4
  • 方案三: 单例组件名

    只拥有单个活跃实例的组件以 The 前缀命名,以示其唯一性,但这并不是因为这组件只可只用于一个单页面,而是每个页面只用一次,这些组件永远不接受任何 prop ,因为他们是为你的应用定制的,而不是应用的上下文

components
=>TheHeading.vue
=>TheSidebar.vue
1
2
3
  • 方案四: 紧密耦合的组件名

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

components
=>TodoList.vue
=>TodoListItem.vue
=>TodoLiisItemButton
1
2
3
4
  • 方案五: 组件名中的单词顺序

    组件名应该以更高级别的(通常是一般化的描述)单词开头,以描述新的修饰符结尾

    >
compenents
=>SearchButtonClear.vue
=>SearchButtonRun.vue
=>SearchInputQuery.vue
1
2
3
4
  • 方案六 : 完整单词的组件名

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

components
=>UserProfileOptions.vue
1
2
  • 方案七 : 组件为多个单词

组件名应该始终有多个单词组成,根组件 App 除外.这样做可以避免与现有的以及未来的 HTML 冲突

Vue.component('todo-item',{
    
    })
1
2
3
  • 方案八: 模板中的组件名大小写

    单文件组件和字符串模板的组件名应该总是单词首字母大写
    但是在 DOM 模板中总是横线连接的

<template>
    <my-component></my-component>
    <MyComponent></MyComponent>
</template>
1
2
3
4
  • 方案九: 自闭合组件

    在单文件组件,字符串模板和 JSX 中没有内容的组件应该是自闭合的,但是在 DOM 模板中永远不要这样做。
    js/jsx中的组件名应该始终都是PascalCase的,尽管在较为简单的应用中只使用Vue.component进行全局组件注册时,可以使用kebab-case字符串。

<template>
    <!--在单文件组件,字符串模板,JSX中-->
    <MyComponent />
    
    <!--在DOM模板中-->
    <my-component></my-component>
</template>
1
2
3
4
5
6
7

# 2.组件里的多个特性元素的撰写格式

多个特性元素应该分多行撰写,每个特性一行. 方便易读

<template>
    <MyComponent
        foo="1"
        bar="2"
        seo="3"> 
    </MyComponent>
</template>
1
2
3
4
5
6
7

# 3.组件的data必须是一个函数

当在组件中使用 data 属性的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数。
当 data 的值是一个对象时,它会在这个组件的所有实例之间共享。想象一下,假如一个 TodoList 组件的数据是这样的:

<script>
export default{
    data: {
        listTitle: '',
        todos: []
    }
}
</script>
1
2
3
4
5
6
7
8

当我们重复使用这个组件的时候,就会产生问题。因为每个组件的实例都引用了相同的数据对象,更改其中一个列表的标题就会改变其它每一个列表的标题。
而这与我们预期的实现效果并不同。我们希望的是每个组件实例都管理自己的数据,因此:每个实例必须生成一个独立的数据对象。在data函数中返回这个对象即可。

<script>
export default{
    data: function(){
        reuturn {
            listTitle: '',
            todos: []
        }
    }
}
</script>
1
2
3
4
5
6
7
8
9
10
<script>
export default{
    data() {
        reuturn {
            listTitle: '',
            todos: []
        }
    }
}
</script>
1
2
3
4
5
6
7
8
9
10
//在一个vue的根实例上直接使用对象是可以的
//因为只存在一个这样的实例
new Vue({
    data: {
        listTitle: '',
        todos: []
    }
})
1
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>   
1
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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# (三) 样式

# 1.为组件样式设置作用域

通过 scoped 特性来设置组件样式作用域

<template>
<style scpoed>
    /* 样式代码 只在当前组件内生效 */
</style>
</template>
1
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>
1
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>
1
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>
1
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>
1
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>
1
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>
1
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>
1
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>
1
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>
1
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>
1
2
3
4
5
6
7
8

# (八) 私有属性名

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

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

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

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

var myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update: function () {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8

# (九) 带引号的特性值

非空 HTML 特性值应该始终带引号 (单引号或双引号,选你 JS 里不用的那个)
在 HTML 中不带空格的特性值是可以没有引号的,但这样做常常导致带空格的特征值被回避,导致其可读性变差

<template>
    <AppSidebar :style="{ width: sidebarWidth + 'px' }">
    <input type="text">
</template>
1
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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Last Updated: 6/29/2020, 9:14:27 AM