提示

vue大部分规范需参照HTML、CSS、JavaScript规范

# 组件名应该始终是多个单词的

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

// 不推荐
Vue.component('todo', {
  // ...
})
export default {
  name: 'Todo',
  // ...
}
// 推荐
Vue.component('todo-item', {
  // ...
})

export default {
  name: 'TodoItem',
  // ...
}

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

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

// 不推荐
Vue.component('some-comp', {
  data: {
    foo: 'bar'
  }
})
export default {
  data: {
    foo: 'bar'
  }
}

// 推荐
Vue.component('some-comp', {
  data: function () {
    return {
      foo: 'bar'
    }
  }
})

// In a .vue file
export default {
  data () {
    return {
      foo: 'bar'
    }
  }
}

// 在一个 Vue 的根实例上直接使用对象是可以的,
// 因为只存在一个这样的实例。
new Vue({
  data: {
    foo: 'bar'
  }
})

# Prop 定义应该尽量详细

至少需要指定其类型(type)。

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

// 推荐
props: {
  status: String
}

// 更好的做法!
props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].indexOf(value) !== -1
    }
  }
}

# 为 v-for 设置键值

总是用 key 配合 v-for。

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

<!-- 推荐 -->
<ul>
  <li v-for="todo in todos" :key="todo.id">
    {{ todo.text }}
  </li>
</ul>

# 避免 v-if 和 v-for 用在一起

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

  • 为了过滤一个列表中的项目 (比如 v-for=“user in users” v-if=“user.isActive”)。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。
  • 为了避免渲染本应该被隐藏的列表 (比如 v-for=“user in users” v-if=“shouldShowUsers”)。这种情形下,请将 v-if 移动至容器元素上 (比如 ul、ol)。
<!-- 不推荐 -->
<ul>
  <li v-for="user in users" v-if="user.isActive" :key="user.id">
    {{ user.name }}
  </li>
</ul>
<ul>
  <li v-for="user in users" v-if="shouldShowUsers" :key="user.id">
    {{ user.name }}
  </li>
</ul>

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

# 为组件样式设置作用域

对于应用来说,顶级 App 组件和布局组件中的样式可以是全局的,但是其它所有组件都应该是有作用域的。 设置样式作用域方法:

  • scoped attribute;
  • CSS Modules(基于 class 的类似 BEM 的策略)
<!-- 不推荐 -->
<template>
  <button class="btn btn-close">X</button>
</template>

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

<!-- 推荐 -->

<!-- 使用 `scoped` attribute -->
<template>
  <button class="button button-close">X</button>
</template>

<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

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

<!-- 使用 CSS Modules -->
<template>
  <button :class="[$style.button, $style.buttonClose]">X</button>
</template>

<style module>
.button {
  border: none;
  border-radius: 2px;
}

.buttonClose {
  background-color: red;
}
</style>

<!-- 使用 BEM 约定 -->
<template>
  <button class="c-Button c-Button--close">X</button>
</template>

<style>
.c-Button {
  border: none;
  border-radius: 2px;
}

.c-Button--close {
  background-color: red;
}
</style>

# 组件文件

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

// 不推荐
Vue.component('TodoList', {
  // ...
})

Vue.component('TodoItem', {
  // ...
})

// 推荐
components/
|- TodoList.js
|- TodoItem.js

components/
|- TodoList.vue
|- TodoItem.vue

# 单文件组件文件的大小写

单文件组件的文件名应该:

  • 始终是单词大写开头 (PascalCase);
  • 始终是横线连接 (kebab-case)。
// 不推荐
components/
|- mycomponent.vue

components/
|- myComponent.vue

// 推荐
components/
|- MyComponent.vue

components/
|- my-component.vue

# 基础组件名

应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如 Base、App 或 V。 这些组件为你的应用奠定了一致的基础样式和行为。它们可能只包括:

  • HTML 元素;
  • 其它基础组件;
  • 第三方 UI 组件库.

好处:

  • 基础组件会全部列在一起,这样更容易识别;
  • 可以避免你在包裹简单组件时随意选择前缀 (比如 MyButton、VueButton);
  • 因为这些组件会被频繁使用,所以你可能想把它们放到全局而不是在各处分别导入它们。
// 不推荐
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue

// 推荐
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue

components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue

components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue

# 单例组件名

  • 只应该拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。
  • 这不意味着组件只可用于一个单页面,而是每个页面只使用一次。
// 不推荐
components/
|- Heading.vue
|- MySidebar.vue

// 推荐
components/
|- TheHeading.vue
|- TheSidebar.vue

# 组件名中的单词顺序

  • 组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾。
  • 如果是一组组件,建议以 高级别的 (通常是一般化描述的) 单词 为名创建一个文件夹,统一放置相关组件文件。
// 不推荐
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue

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

components/
|- search/
  |- ButtonClear.vue
  |- ButtonRun.vue
  |- InputQuery.vue
  |- InputExcludeGlob.vue
|-setting/
  |- CheckboxTerms.vue
  |- CheckboxLaunchOnStartup.vue

# 自闭合组件

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

<!-- 不推荐 -->

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

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

<!-- 推荐 -->

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

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

# 自模板中的组件名大小写

  • 对于绝大多数项目来说,在单文件组件和字符串模板中组件名应该总是 PascalCase 的——但是在 DOM 模板中总是 kebab-case 的。
  • 在所有地方使用 kebab-case
<!-- 不推荐 -->

<!-- 在单文件组件和字符串模板中 -->
<mycomponent/>

<!-- 在单文件组件和字符串模板中 -->
<myComponent/>

<!-- 在 DOM 模板中 -->
<MyComponent></MyComponent>

<!-- 推荐 -->

<!-- 在单文件组件和字符串模板中 -->
<MyComponent/>

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

<!-- 在所有地方 -->
<my-component/>

# JS/JSX 中的组件名大小写

JS/JSX 中的组件名应该始终是 PascalCase 的,尽管在较为简单的应用中只使用 Vue.component 进行全局组件注册时,可以使用 kebab-case 字符串。

// 不推荐
Vue.component('myComponent', {
  // ...
})

import myComponent from './MyComponent.vue'
export default {
  name: 'myComponent',
  // ...
}
export default {
  name: 'my-component',
  // ...
}

// 推荐
Vue.component('MyComponent', {
  // ...
})

Vue.component('my-component', {
  // ...
})

import MyComponent from './MyComponent.vue'

export default {
  name: 'MyComponent',
  // ...
}

# 完整单词的组件名

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

// 不推荐
components/
|- SdSettings.vue
|- UProfOpts.vue

// 推荐
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

# Prop 名大小写

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

// 不推荐
props: {
  'greeting-text': String
}

<welcome-message greetingText="hi"/>

// 推荐
props: {
  greetingText: String
}

<welcome-message greeting-text="hi"/>

# 多个 attribute 的元素

多个 attribute 的元素应该一行撰写,超出 80 字符按照attribute元素进行换行。

<!-- 不推荐 -->
<img
  src="https://vuejs.org/images/logo.png"
  alt="Vue Logo"
>

<my-component
  foo="a"
  bar="b"
  baz="c"
/>

<!-- 推荐 -->
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">

<my-component foo="a" bar="b" baz="c"/>

# 模板中简单的表达式

  • 组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。
  • 前要空一格
<!-- 不推荐 -->
<div>
  {{
    fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }}
</div>

<!-- 推荐 -->
<div>{{ normalizedFullName }}</div>

computed: {
  normalizedFullName: function () {
    return this.fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}

# 简单的计算属性

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

// 不推荐
computed: {
  price: function () {
    var basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice - basePrice * (this.discountPercent || 0)
    )
  }
}

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

# 带引号的 attribute 值

非空 HTML attribute 值应该始终带双引号 。

<!-- 推荐 -->
<input type="text">
<app-sidebar :style="{ width: sidebarWidth + 'px'}"/>

# 指令缩写

指令缩写 (用 : 表示 v-bind:、用 @ 表示 v-on: 和用 # 表示 v-slot:)

<!-- 不推荐 -->
<input v-bind:value="newTodoText" :placeholder="newTodoInstructions">

<input v-on:input="onInput" @focus="onFocus">

<template v-slot:header>
  <h1>Here might be a page title</h1>
</template>

<!-- 推荐 -->
<input :value="newTodoText" :placeholder="newTodoInstructions">

<input @input="onInput" @focus="onFocus">

<template #header>
  <h1>Here might be a page title</h1>
</template>

# 组件/实例的选项的统一顺序

  • 1.副作用 (触发组件外的影响);(el)
  • 2.全局感知 (要求组件以外的知识);(name/parent)
  • 3.组件类型 (更改组件的类型);(functional)
  • 4.模板修改器 (改变模板的编译方式);(delimiters/comments)
  • 5.模板依赖 (模板内使用的资源);(components/directives/filters)
  • 6.组合 (向选项里合并 property);(extends/mixins)
  • 7.接口 (组件的接口);(inheritAttrs/model/props/propsData)
  • 8.本地状态 (本地的响应式 property);(data/computed)
  • 9.事件 (通过响应式事件触发的回调);(watch/生命周期钩子)
  • .生命周期钩子顺序:beforeCreate / created / beforeMount / mounted / beforeUpdate / updated / activate d /deactivated / beforeDestroy / destroyed
  • 10.非响应式的 property (不依赖响应系统的实例 property);(methods)
  • 11.渲染 (组件输出的声明式描述)。(template/render/renderError)

最常用组件/实例顺序:

  • 1.全局感知 (要求组件以外的知识);(name/parent)
  • 2.模板修改器 (改变模板的编译方式);(delimiters/comments)
  • 3.模板依赖 (模板内使用的资源);(components/directives/filters)
  • 4.接口 (组件的接口);(inheritAttrs/model/props/propsData)
  • 5.本地状态 (本地的响应式 property);(data/computed)
  • 6.事件 (通过响应式事件触发的回调);(watch/生命周期钩子)
  • 生命周期钩子顺序:beforeCreate / created / beforeMount / mounted / beforeUpdate / updated / activate d /deactivated / beforeDestroy / destroyed
  • 7.非响应式的 property (不依赖响应系统的实例 property)。(methods)

# 元素 attribute 的顺序

  • 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.唯一的 attribute (需要唯一值的 attribute);(ref/key)
  • 7.双向绑定 (把绑定和事件结合起来);(v-model)
  • 8.事件 (组件事件监听器);(v-on)
  • 9.内容 (覆写元素的内容);(v-html/v-text)
  • 10.其它 attribute (所有普通的绑定或未绑定的 attribute)。

# 组件/实例选项中的空行

一组 property之间,空行隔开

// 推荐
props: {
  value: {
    type: String,
    required: true
  },

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

  label: String,
  icon: String
},

data() {
  return {
    advertInfo:[],
    advertConfig:{},
    
    other1:'',
    other2:'',
  }
},

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

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

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

<!-- 推荐 -->
<template>...</template>

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

<style>/* ... */</style>

# 避免在scoped 中使用元素选择器

<!-- 不推荐 -->
<template>
  <button>X</button>
</template>

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

<!-- 推荐 -->
<template>
  <button class="btn btn-close">X</button>
</template>

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

# 隐性的父子组件通信

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

# 非 Flux 的全局状态管理

应该优先通过 Vuex 管理全局状态,而不是通过 this.$root 或一个全局事件总线。

# Vue Router 规范

# 页面跳转数据传递使用路由参数

let id = '123'
this.$router.push({ name: 'userCenter', query: { id: id } })

# router 中的命名规范

  • path、childrenPoints 命名规范采用kebab-case命名规范;
  • path必须以 / 开头,children里的path也要以 / 开头;
  • name 命名规范采用KebabCase命名规范且和component组件名保持一致。

# 组件内方法变量命名规范

  • 查询列表方法默认命名:getList,如果同一组件内存在多个查询列表方法命名:get+业务名称+List
  • 查询列表方法(分页)默认命名:pageList,如果同一组件内存在多个查询列表方法命名:page+业务名称+List
  • 新增、修改、删除等操作以handle为前缀,例如handleAdd、handleUpdate、handleDelete等
  • 初始化方法应该命名为:init
  • 保存表单方法应命名为:submitForm
  • 显隐状态命名应以:业务+Visible
  • 待补充...

# 注释规范

  • 每个独立的VUE文件开头都要进行注释,表明该文件的描述信息、作者、创建时间等
<!--
 * @FileDescription: 该文件的描述信息
 * @Author: 作者信息
 * @Date: 文件创建时间
 * @LastEditors: 最后更新作者
 * @LastEditTime: 最后更新时间
 -->
  • VUE文件中method中的方法注释
 /**
  * @description: 方法描述
  * @param {参数类型} 参数名称
  * @param {参数类型} 参数名称
  * @return 没有返回信息写 void / 有返回信息 {返回类型} 描述信息
  */

# 全局组件

  • 当组件在多个场景下使用,组件放到components目录下并注册为全局组件

# 格式化

  • 如果开发工具使用的是vscode,请在设置中进行如下配置
 {
  "editor.fontSize": 18,
  "workbench.colorTheme": "Visual Studio Dark",
  "update.mode": "manual",
  "eslint.codeAction.showDocumentation": {
    "enable": true
  },
  "eslint.execArgv": null,
  "search.followSymlinks": false,
  "editor.tabSize": 2, //配置eslint
  "eslint.validate": [
    "javascript", // 用eslint的规则检测js文件
    "javascriptreact",
    "vue",
    "html"
  ], // 启用保存时自动修复eslint,默认只支持.js文件
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "eslint.codeActionsOnSave.rules": null
}
上次更新时间: 5/4/2023, 10:08:27 AM