Vue代码规范

Vue代码规范

认识代码规范

先来思考两个问题:

  1. 什么是代码规范?
  2. 为什么需要代码规范?

如果你是一个经验丰富的前端开发,你一定接触过这样的老项目:变量名是 abc,fds 这种随意起的,或者是 name1, name2 这种带数字起名,这样的变量不加注释,鬼都不知道它是干什么的。

这类代码就是一种典型的不规范代码。这样的代码除了让我们开发人员情绪暴躁,最重要的问题是,它极大的降低了团队协作的效率和程序质量。

在团队协作过程中,当组内其他人需要使用或 review 你的代码,看到这种情况,除了喷你,还要花费大量时间了解你写的是什么。同时这样非常容易造成变量冲突,带来未知隐患,调试困难等问题,甚至可以看出一个程序员的编码态度和专业程度。

当然,代码规范包含很多方面,变量命名规范只是最基础的规范。不规范的地方越多,程序质量越低,团队协作的效率也就会越低。

工程目录

  • 全局通用的组件放在 /src/components/ 下。
  • 页面/视图组件放在 /src/views/ 下。
  • 全局公共指令、过滤器(多于三个文件以上引用)分别放在 src/ 目录下的 directives/、filters/
  • 当页面文件具有私有组件、指令、过滤器时,则建立一个与页面同名的目录,页面文件更名为 index.vue,然后在该目录下创建私有 ./components 等文件夹。

例如:

src/
├── App.vue
├── assets
├── main.js
├── components                     # 全局通用组件
├── directive                      # 全局公共指令
├── filters                        # 全局公共过滤器
└── views                          # 页面/视图
    ├── login
    │   ├── components             # 私有组件
    │   └── index.vue
    └── profile
        ├── components             # 私有组件
        └── index.vue

命名规范

1.1 通用文件与文件夹

1.1.0 市面上常用的命名规范
  • camelCase(小驼峰式命名法 —— 首字母小写)
  • PascalCase(大驼峰式命名法 —— 首字母大写)
  • kebab-case(短横线/中划线命名法)
  • Snake(下划线命名法)
1.1.1 项目名

全部采用小写方式, 以短横线分隔。例:my-project-name

1.1.2 目录名

参照项目命名规则,有复数结构时,要采用复数命名法。例:docs、assets、components、directives、mixins、utils、views。

1.1.3 图片文件名

全部采用小写方式, 优先选择单个单词命名,多个单词命名以下划线分隔。

banner_sina.gif
menu_aboutus.gif
menutitle_news.gif
logo_police.gif
logo_national.gif
pic_people.jpg
1.1.4 HTML 文件名

全部采用小写方式, 优先选择单个单词命名,多个单词命名以下划线分隔。

|- error_report.html
|- success_report.html
1.1.5 CSS 文件名

全部采用小写方式, 优先选择单个单词命名,多个单词命名以短横线分隔。

|- normalize.less
|- base.less
|- date-picker.scss
|- input-number.scss

1.1.6 JavaScript 文件名

全部采用小写方式, 优先选择单个单词命名,多个单词命名以短横线分隔。

|- index.js
|- plugin.js
|- util.js
|- date-util.js
|- account-model.js
|- collapse-transition.js

上述规则可以快速记忆为“静态文件下划线,编译文件短横线”。

1.2 Vue特色文件夹

在 Vue 工程中,这是对通用文件与文件夹命名规范的补充。

1.2.1 位于 components/ 下的直接子文件夹
  • 代表着这类模块的类别。
  • 由名词组成(例如:Charts/)。
  • 最好只有一个单词,特殊情况可例外(good: Car/,Order/,Cart/)(bad: CarInfo/,CarPage/)。
  • 如果有两个以上单词,采用 PascalBase 大驼峰 风格(例如:BackToTop/)
1.2.2 位于 views/ 下的文件夹和其它文件夹
  • 代表着页面的名字,或者类名。
  • 由名词组成(例如:profile/)。
  • 如果有两个以上单词,采用 kebab-case 短横线 的风格(例如:error-page/)。

例如:

src/
├── App.vue
├── assets
├── main.js
├── components
│   ├── BackToTop             # 通用组件的文件夹
│   └── Charts                # 通用组件的文件夹
│       ├── Keyboard.vue
│       ├── LineMarker.vue
│       ├── MixChart.vue
│       └── mixins            # 其它文件夹(不是 components/ 的直接子文件夹)
├── directive
│   ├── clipboard             # 其它文件夹
│   └── permission            # 其它文件夹
├── filters
└── views
    ├── charts                # 视图组件的文件夹
    ├── clipboard             # 视图组件的文件夹
    ├── dashboard             # 视图组件的文件夹
    │   ├── admin             # 视图组件的文件夹
    │   │   ├── components
    │   │   └── index.vue
    │   ├── editor            # 视图组件的文件夹
    │   │   └── index.vue
    │   └── index.vue
    └── error-page            # 视图组件的文件夹
        ├── 401.vue
        └── 404.vue

1.3 Vue 组件命名

1.3.1 单文件组件名

文件扩展名为 .vue 的 single-file components(单文件组件)。单文件组件名应该始终是单词大写开头(PascalCase 大驼峰)。

我们这里说的大驼峰命名的组件指的是components/文件夹下的可复用的公用组件(非页面组件),也包括 views/视图文件夹下的私有 components/文件夹下的私有组件。但不包括 views/ 文件夹下的页面组件,页面视图组件采用kebab-case 的风格。

推荐:

src/
├── MyComponent.vue
为什么选择大驼峰(PascalCase)作为components组件的命名格式?
  1. PascalCase 是合法的 JavaScript 标识符。这使得在 JavaScript 中导入和注册组件都很容易,同时 IDE 也能提供较好的自动补全。
  2. <PascalCase /> 在模板中更明显地表明了这是一个 Vue 组件,而不是原生 HTML 元素。同时也能够将 Vue 组件和自定义元素 (web components) 区分开来。

在单文件组件和内联字符串模板中,我们都推荐这样做。但是,PascalCase 的标签名在 DOM 模板中是不可用的,详情参见 DOM 模板解析注意事项

为了方便,Vue 支持将模板中使用 kebab-case 的标签解析为使用 PascalCase 注册的组件。这意味着一个以 MyComponent 为名注册的组件,在模板中可以通过 <MyComponent><my-component> 引用。这让我们能够使用同样的 JavaScript 组件注册代码来配合不同来源的模板。

1.3.2 单例组件名

只拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。

这不意味着组件只可用于一个单页面,而是_每个页面_只使用一次。这些组件永远不接受任何 prop,因为它们是为你的应用定制的。如果你发现有必要添加 prop,那就表明这实际上是一个可复用的组件,_只是目前_在每个页面里只使用一次。

比如,头部和侧边栏组件几乎在每个页面都会使用,不接受 prop,该组件是专门为该应用所定制的。

推荐:

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

不推荐:

components/
├── Heading.vue
├── MySidebar.vue
1.3.3 基础组件名

基础组件:不包含业务,独立、具体功能的基础组件,比如日期选择器、模态框等。这类组件作为项目的基础控件,会被大量使用,因此组件的 API
进行过高强度的抽象,可以通过不同配置实现不同的功能。

应用特定样式和约定的基础组件(也就是展示类的、无逻辑的或无状态、不掺杂业务逻辑的组件) 应该全部以一个特定的前缀开头,比如 Base、App 或 V。

基础组件在一个页面内可使用多次,在不同页面内也可复用,是高可复用组件。

推荐:

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

不推荐:

components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
1.3.4 业务组件

业务组件:它不像基础组件只包含某个功能,而是在业务中被多个页面复用的(具有可复用性),它与基础组件的区别是,业务组件只在当前项目中会用到,不具有通用性,而且会包含一些业务,比如数据请求;而基础组件不含业务,在任何项目中都可以使用,功能单一,比如一个具有数据校验功能的输入框。

掺杂了复杂业务的组件(拥有自身 data、prop 的相关处理)即业务组件。应该以Custom前缀命名。

业务组件在一个页面内比如:某个页面内有一个卡片列表,而样式和逻辑跟业务紧密相关的卡片就是业务组件。

推荐:

components/
|- custom-card.vue
1.3.5 紧密耦合的组件名

该类组件只在某个父组件的场景下有意义,这层关系应该体现在其名字上。因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起。

和父组件紧密耦合的子组件一般以父组件名作为前缀命名

推荐:

components/
├── TodoList.vue
├── TodoListItem.vue
├── TodoListItemButton.vue

不推荐:

components/
├── TodoList.vue
├── ItemForTodoList.vue
├── ButtonForTodoListItem.vue
1.3.6 组件名中单词顺序

组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾。 因为编辑器通常会按字母顺序组织文件,所以现在组件之间的重要关系一目了然。

这个表达比较抽象,下面以搜索和设置功能的组件为例,解释什么叫「高级别」。

推荐:

components/
├── SearchButtonClear.vue
├── SearchButtonRun.vue
├── SearchInputQuery.vue
├── SearchInputExcludeGlob.vue
├── SettingsCheckboxTerms.vue
├── SettingsCheckboxLaunchOnStartup.vue

不推荐:

components/
├── ClearSearchButton.vue
├── ExcludeFromSearchInput.vue
├── LaunchOnStartupCheckbox.vue
├── RunSearchButton.vue
├── SearchInput.vue
├── TermsCheckbox.vue

还有另一种多级目录的方式,把所有的搜索组件放到 search/ 目录,把所有的设置组件放到 settings/ 目录。我们只推荐在非常大型(如有 100+ 个组件)的应用下才考虑这么做,因为在多级目录间找来找去,要比在单个 components 目录下滚动查找花费的精力更多。

1.3.7 组件名必须是完整单词,禁用缩写

组件名应该倾向于完整单词而不是缩写,这样便于团队合作与后期维护。编辑器中的自动补全已经让书写长命名的代价非常之低了,而其带来的明确性却是非常宝贵的。不常用的缩写尤其应该避免。

推荐:

components/
├── StudentDashboardSettings.vue
├── UserProfileOptions.vue

不推荐:

components/
├── SdSettings.vue
├── UProfOpts.vue
1.3.8 页面组件名

视图文件夹:位于 views/ 文件夹下,代表着一类页面的名字,存放视图组件。
视图组件:代表着某一页面的名字,或者类名。
视图组件与其它组件的区别是:视图组件位于 view/ 文件夹或视图文件夹下,其它组件位于 components/ 文件夹下。

  • 视图组件可以直接位于 views/ 文件夹下,例如:Login.vue、Home.vue。
  • 视图文件夹的名字要代表着页面的名字,例如:login/、error-page/。
  • 视图文件夹下只有一个视图组件的时候,该组件取名为 index.vue,例如:login/index.vue。
  • 视图文件夹下有两个以上视图组件的时候,分别取名,要体现内部文件的所属类名,例如:car/car-list.vue 和 car/car-detail.vue。
  • 尽量是名词
  • 常用结尾单词有(detail、edit、list、info、report)。
  • 视图文件夹下可以存在私有 components/,其内部 .vue 文件遵循其它组件的命名方式(大驼峰)。
  • 除 components/ 下的,一律采用 kebab-case 的风格。

例如:

src/
├── components
│   ├── BackToTop               # 通用组件的文件夹
│   └── Charts                  # 通用组件的文件夹
│       ├── Keyboard.vue
│       ├── LineMarker.vue
│       ├── MixChart.vue
│       └── mixins              # 其它文件夹
└── views
    ├── login                   # 视图组件的文件夹
    │   └── index.vue
    ├── profile                 # 视图组件的文件夹
    │   ├── index.vue
    │   └── components
    ├── car                     # 视图组件的文件夹
    │   ├── car-list.vue        # 视图组件
    │   ├── car-detail.vue      # 视图组件
    │   └── components
    │       ├── CarListItem.vue
    │       └── CarInfoItem.vue
    └── error-page              # 视图组件的文件夹
        ├── 401.vue             # 视图组件
        └── 404.vue             # 视图组件

1.4 Vue 组件内部参数命名

1.4.1 name

组件名应该始终是多个单词,应该始终是 PascalCase 大驼峰命名。 根组件 App 以及 <transition>、<component> 之类的 Vue 内置组件除外。这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的。

推荐:

export default {
  name: 'ToDoList',
  // ...
}
1.4.2 prop

在声明 prop 的时候,其命名应该始终使用 camelCase小驼峰,而在模板和 JSX 中应该始终使用 kebab-case短横线。我们单纯的遵循每个语言的约定,在 JavaScript 中更自然的是 camelCase。而在 HTML 中则是 kebab-case。

推荐:

//template 模板中
<welcome-message greeting-text="hi"/>
//<script>中
 props: {
  // 组件状态,用于控制组件的颜色
   status: {
     greetingText: String,
     required: true,
     validator: function (value) {
       return [
         'succ',
         'info',
         'error'
       ].indexOf(value) !== -1
     }
   },
 }

不推荐:

  // 错误:greetingText不要用小驼峰,应该用短横线greeting-text
<welcome-message greetingText="hi"/>
props: {
  // 错误:1,不要用短横线,应该用小驼峰greetingText
  // 错误:2,不要用引号
'greeting-text': String
}
1.4.3 router

Vue Router Path 命名采用 kebab-case 格式。 用 Snake下划线(如:/user_info)或 camelCase小驼峰(如:/userInfo)的单词会被当成一个单词,搜索引擎无法区分语义。

推荐:

// good
{
  path: '/user-info', // // user-info 能被搜索引擎解析成 user info
  name: 'UserInfo',
  component: UserInfo,
  meta: {
    title: ' - 用户',
    desc: ''
  }
},

不推荐:

// bad
{
  path: '/user_info', // // user_info 被搜索引擎当成一个单词
  name: 'UserInfo',
  component: UserInfo,
  meta: {
    title: ' - 用户',
    desc: ''
  }
},
1.4.4 模板中组件

对于绝大多数项目来说,在单文件组件和字符串模板中组件名应该总是 PascalCase 的——但是在 DOM 模板中总是 kebab-case 的。

推荐:

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

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

1.4.5 自闭合组件

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

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

<!-- 在所有地方 -->
<my-component></my-component>
1.4.6 变量
  • 命名方法:camelCase
  • 命名规范:类型 + 对象描述或属性的方式
// bad
var getTitle = "LoginTable"

// good
let tableTitle = "LoginTable"
let mySchool = "我的学校"
1.4.7 常量
  • 命名方法:全部大写下划线分割
  • 命名规范:使用大写字母和下划线来组合命名,下划线用以分割单词

推荐:

const MAX_COUNT = 10
const URL = 'https://www.fedbook.cn'
1.4.8 方法
  • 命名方法:camelCase 小驼峰
  • 命名规范:统一使用动词或者动词 + 名词形式

1)普通情况下,使用动词 + 名词形式

推荐:jumpPage、openCarInfoDialog

不推荐:go、nextPage、show、open、login

2)请求数据方法,以 data 结尾

推荐:getListData、postFormData

不推荐:takeData、confirmData、getList、postForm

3)单个动词的情况

推荐:init、refresh

动词含义返回值
can判断是否可执行某个动作 (权 )函数返回一个布尔值。true:可执行;false:不可执行;
has判断是否含有某个值函数返回一个布尔值。true:含有此值;false:不含有此值;
is判断是否为某个值函数返回一个布尔值。true:为某个值;false:不为某个值;
get获取某个值函数返回一个非布尔值
set设置某个值无返回值、返回是否设置成功或者返回链式对象

特此说明,增删查改、详情统一使用如下 5 个单词,不得使用其他(目的是为了统一各个端)

add / update / delete / **detail / get

附: 函数方法常用的动词:

get 获取/set 设置, 
add 增加/remove 删除, 
create 创建/destory 销毁, 
start 启动/stop 停止, 
open 打开/close 关闭, 
read 读取/write 写入, 
load 载入/save 保存,
begin 开始/end 结束, 
backup 备份/restore 恢复,
import 导入/export 导出, 
split 分割/merge 合并,
inject 注入/extract 提取,
attach 附着/detach 脱离, 
bind 绑定/separate 分离, 
view 查看/browse 浏览, 
edit 编辑/modify 修改,
select 选取/mark 标记, 
copy 复制/paste 粘贴,
undo 撤销/redo 重做, 
insert 插入/delete 移除,
add 加入/append 添加, 
clean 清理/clear 清除,
index 索引/sort 排序,
find 查找/search 搜索, 
increase 增加/decrease 减少, 
play 播放/pause 暂停, 
launch 启动/run 运行, 
compile 编译/execute 执行, 
debug 调试/trace 跟踪, 
observe 观察/listen 监听,
build 构建/publish 发布,
input 输入/output 输出,
encode 编码/decode 解码, 
encrypt 加密/decrypt 解密, 
compress 压缩/decompress 解压缩, 
pack 打包/unpack 解包,
parse 解析/emit 生成,
connect 连接/disconnect 断开,
send 发送/receive 接收, 
download 下载/upload 上传, 
refresh 刷新/synchronize 同步,
update 更新/revert 复原, 
lock 锁定/unlock 解锁, 
check out 签出/check in 签入, 
submit 提交/commit 交付, 
push 推/pull 拉,
expand 展开/collapse 折叠, 
enter 进入/exit 退出,
abort 放弃/quit 离开, 
obsolete 废弃/depreciate 废旧, 
collect 收集/aggregate 聚集
1.4.9 自定义事件

自定义事件应始终使用 kebab-case 的事件名

不同于组件和 prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。

this.$emit('my-event')
<MyComponent @my-event="handleDoSomething" />

不同于组件和 prop,事件名不会被用作一个 JavaScript 变量名或 property 名,所以就没有理由使用 camelCase 或 PascalCase 了。并且 v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。

  • 原生事件参考列表

由原生事件可以发现其使用方式如下:

<div
  @blur="toggleHeaderFocus"
  @focus="toggleHeaderFocus"
  @click="toggleMenu"
  @keydown.esc="handleKeydown"
  @keydown.enter="handleKeydown"
  @keydown.up.prevent="handleKeydown"
  @keydown.down.prevent="handleKeydown"
  @keydown.tab="handleKeydown"
  @keydown.delete="handleKeydown"
  @mouseenter="hasMouseHoverHead = true"
  @mouseleave="hasMouseHoverHead = false">
</div>

而为了区分原生事件自定义事件在 Vue 中的使用,建议除了多单词事件名使用 kebab-case 的情况下,命名还需遵守为 on + 动词 的形式,如下:

<!-- 父组件 -->
<div
  @on-search="handleSearch"
  @on-clear="handleClear"
  @on-clickoutside="handleClickOutside">
</div>

// 子组件
export default {
  methods: {
    handleTriggerItem () {
      this.$emit('on-clear')
    }
  }
}
1.4.10 事件方法
  • 命名方法:camelCase
  • 命名规范:handle + 名称(可选)+ 动词
<template>
  <div
    @click.native.stop="handleItemClick()"
    @mouseenter.native.stop="handleItemHover()">
  </div>
</template>

<script>

export default {
  methods: {
    handleItemClick () {
      //...
    },
    handleItemHover () {
      //...
    }
  }
}
</script>

编码规范

2.1 Vue项目规范

2.1.1 代码结构
<template>
  <div id="my-component">
    <DemoComponent />
  </div>
</template>

<script>
import DemoComponent from '../components/DemoComponent'

export default {
  name: 'MyComponent',
  components: {
    DemoComponent
  },
  mixins: [],
  props: {},
  data () {
    return {}
  },
  computed: {},
  watch: {}
  created () {},
  mounted () {},
  destroyed () {},
  methods: {},
}
</script>

<style lang="scss" scoped>
#my-component {
}
</style>
2.1.2 template模板
2.1.2.1 为 v-for 设置键值

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

<ul>
  <li
    v-for="todo in todos"
    :key="todo.id">
      {{ todo.text }}
  </li>
</ul>
2.1.2.2 v-if 和 v-for 互斥

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

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

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

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

computed: {
  activeUsers: function () {
    return this.users.filter((user) => {
      return user.isActive
    })
  }
}
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id">
      {{ user.name }}
  </li>
</ul>

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

<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>
2.1.2.3 v-show 与 v-if 选择

如果运行时,需要非常频繁地切换,使用 v-show ;如果在运行时,条件很少改变,使用 v-if。

2.1.2.4 多个 attribute 的元素

多个 attribute 的元素应该分多行撰写,每个 attribute 一行。

<!-- bad -->
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent foo="a" bar="b" baz="c"/>

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

<MyComponent
  foo="a"
  bar="b"
  baz="c"/>
2.1.2.5 模板中简单的表达式

组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法

复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。

不推荐:

<!-- 在模板中 -->
{{
  fullName.split(' ').map((word) => {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')
}}

推荐:

<!-- 在模板中 -->
{{ normalizedFullName }}

// 复杂表达式已经移入一个计算属性
computed: {
  normalizedFullName: function () {
    return this.fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}
2.1.2.6 带引号的 attribute 值

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

<!-- bad -->
<input type=text>
<AppSidebar :style={width:sidebarWidth+'px'}>

<!-- good -->
<input type="text">
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
2.1.2.7 指令缩写
  • : 表示 v-bind:
  • @ 表示 v-on:
  • # 表示 v-slot:
<input
  :value="newTodoText"
  :placeholder="newTodoInstructions">

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

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

<template #footer>
  <p>Here's some contact info</p>
</template>
2.1.3 data

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

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

Prop 定义应该尽量详细。

  • script中使用 camelCase 小驼峰命名,template中用kebab-case短横线
  • 必须指定类型
  • 必须加上注释,表明其含义
  • 必须加上 required 或者 default,两者二选其一
  • 如果有业务需要,必须加上 validator 验证
//template 模板中
<welcome-message greeting-text="hi"/>
//<script>中
 props: {
  // 组件状态,用于控制组件的颜色
   status: {
     greetingText: String,
     required: true,
     validator: function (value) {
       return [
         'succ',
         'info',
         'error'
       ].indexOf(value) !== -1
     }
   },
 }

不推荐:

  // 错误:greetingText不要用小驼峰,应该用短横线greeting-text
<welcome-message greetingText="hi"/>
props: {
  // 错误:1,不要用短横线,应该用小驼峰greetingText
  // 错误:2,不要用引号
'greeting-text': String
}
2.1.5 computed

应该把复杂计算属性分割为尽可能多的更简单的属性。 小的、专注的计算属性减少了信息使用时的假设性限制,所以需求变更时也用不着那么多重构了。

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

// good
computed: {
  basePrice: function () {
    return this.manufactureCost / (1 - this.profitMargin)
  },
  discount: function () {
    return this.basePrice * (this.discountPercent || 0)
  },
  finalPrice: function () {
    return this.basePrice - this.discount
  }
}
2.1.6 Vue Router 规范
2.1.6.1 页面跳转数据传递使用路由参数

页面跳转,例如 A 页面跳转到 B 页面,需要将 A 页面的数据传递到 B 页面,推荐使用 路由参数进行传参,而不是将需要传递的数据保存 vuex,然后在 B 页面取出 vuex 的数据,因为如果在 B 页面刷新会导致 vuex 数据丢失,导致 B 页面无法正常显示数据。

推荐:

let id = ' 123';
this.$router.push({ name: 'userCenter', query: { id: id } });
2.1.6.2 使用路由懒加载(延迟加载)机制
{
    path: '/uploadAttachment',
    name: 'uploadAttachment',
    meta: {
      title: '上传附件'
    },
    component: () => import('@/view/components/uploadAttachment/index.vue')
  },
2.1.6.3 router 中的命名规范

path、childrenPoints 命名规范采用kebab-case命名规范(尽量vue文件的目录结构保持一致,因为目录、文件名都是kebab-case,这样很方便找到对应的文件)

name 命名规范采用KebabCase命名规范且和component组件名保持一致!(因为要保持keep-alive特性,keep-alive按照component的name进行缓存,所以两者必须高度保持一致)

// 动态加载

export const reload = [
  {
    path: '/reload',
    name: 'reload',
    component: Main,
    meta: {
      title: '动态加载',
      icon: 'icon iconfont'
    },
    children: [
      {
        path: '/reload/smart-reload-list',
        name: 'SmartReloadList',
        meta: {
          title: 'SmartReload',
          childrenPoints: [
            {
              title: '查询',
              name: 'smart-reload-search'
            },
            {
              title: '执行reload',
              name: 'smart-reload-update'
            },
            {
              title: '查看执行结果',
              name: 'smart-reload-result'
            }
          ]
        },
        component: () =>
          import('@/views/reload/smart-reload/smart-reload-list.vue')
      }
    ]
  }
];
2.1.6.4 router 中的 path 命名规范

path除了采用kebab-case命名规范以外,必须以 / 开头,即使是children里的path也要以 / 开头。如下示例

目的:

经常有这样的场景:某个页面有问题,要立刻找到这个vue文件,如果不用以/开头,path为parent和children组成的,可能经常需要在router文件里搜索多次才能找到,而如果以/开头,则能立刻搜索到对应的组件

{
    path: '/file',
    name: 'File',
    component: Main,
    meta: {
      title: '文件服务',
      icon: 'ios-cloud-upload'
    },
    children: [
      {
        path: '/file/file-list',
        name: 'FileList',
        component: () => import('@/views/file/file-list.vue')
      },
      {
        path: '/file/file-add',
        name: 'FileAdd',
        component: () => import('@/views/file/file-add.vue')
      },
      {
        path: '/file/file-update',
        name: 'FileUpdate',
        component: () => import('@/views/file/file-update.vue')
      }
    ]
  }

2.2 HTML规范

2.2.1 文件模板

HTML5 文件模板:

<!DOCTYPE html>
  <html lang="zh-CN">
  <head>
    <meta charset="UTF-8">
    <title>HTML5标准模版</title>
  </head>
  <body>
  </body>
</html>

移动端:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
    <meta name="format-detection" content="telephone=no">
    <title>移动端HTML模版</title>

    <!-- S DNS预解析 -->
    <link rel="dns-prefetch" href="">
    <!-- E DNS预解析 -->

    <!-- S 线上样式页面片,开发请直接取消注释引用 -->
    <!-- #include virtual="" -->
    <!-- E 线上样式页面片 -->

    <!-- S 本地调试,根据开发模式选择调试方式,请开发删除 -->
    <link rel="stylesheet" href="css/index.css">
    <!-- /本地调试方式 -->

    <link rel="stylesheet" href="http://srcPath/index.css">
    <!-- /开发机调试方式 -->
    <!-- E 本地调试 -->

</head>
<body>
</body>
</html>

PC 端:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="keywords" content="your keywords">
    <meta name="description" content="your description">
    <meta name="author" content="author,email address">
    <meta name="robots" content="index,follow">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="renderer" content="ie-stand">
    <title>PC端HTML模版</title>

    <!-- S DNS预解析 -->
    <link rel="dns-prefetch" href="">
    <!-- E DNS预解析 -->

    <!-- S 线上样式页面片,开发请直接取消注释引用 -->
    <!-- #include virtual="" -->
    <!-- E 线上样式页面片 -->

    <!-- S 本地调试,根据开发模式选择调试方式,请开发删除 -->
    <link rel="stylesheet" href="css/index.css">
    <!-- /本地调试方式 -->

    <link rel="stylesheet" href="http://srcPath/index.css">
    <!-- /开发机调试方式 -->
    <!-- E 本地调试 -->
</head>
<body>
</body>
</html>
2.2.2 元素及标签闭合

HTML 元素共有以下5种:

  • 空元素:area、base、br、col、command、embed、hr、img、input、keygen、link、meta、param、source、track、wbr
  • 原始文本元素:script、style
  • RCDATA 元素:textarea、title
  • 外来元素:来自 MathML 命名空间和 SVG 命名空间的元素
  • 常规元素:其他 HTML 允许的元素都称为常规元素

为了能让浏览器更好的解析代码以及能让代码具有更好的可读性,有如下约定:

  • 所有具有开始标签和结束标签的元素都要写上起止标签,某些允许省略开始标签或和束标签的元素亦都要写上。
  • 空元素标签都不加 “/” 字符。
<!-- good -->
<div>
    <h1>我是h1标题</h1>
    <p>我是一段文字,我有始有终,浏览器能正确解析</p>
</div>
 
<br data-tomark-pass>

<!-- bad -->
<div>
    <h1>我是h1标题</h1>
    <p>我是一段文字,我有始无终,浏览器亦能正确解析
</div>

<br/>
2.2.3 代码嵌套

元素嵌套规范,每个块状元素独立一行,内联元素可选。

<!-- good -->
<div>
    <h1></h1>
    <p></p>
</div> 
<p><span></span><span></span></p>

<!-- bad -->
<div>
    <h1></h1><p></p>
</div> 
<p> 
    <span></span>
    <span></span>
</p>

段落元素与标题元素只能嵌套内联元素。

<!-- good -->
<h1><span></span></h1>
<p><span></span><span></span></p>

<!-- bad -->
<h1><div></div></h1>
<p><div></div><div></div></p>

2.3 CSS规范

2.3.1 命名

 类名使用小写字母,以中划线分隔
 id 采用驼峰式命名
 scss 中的变量、函数、混合、placeholder 采用驼峰式命名

ID 和 class 的名称总是使用可以反应元素目的和用途的名称,或其他通用的名称,代替表象和 晦涩难懂的名称。

不推荐:

.fw-800 {
    font-weight: 800;
  }
  .red {
    color: red; 
   }

推荐:

.heavy {
   font-weight: 800;
  }
.important { 
  color: red; 
 }
2.3.2 选择器
1) css 选择器中避免使用标签名

从结构、表现、行为分离的原则来看,应该尽量避免 css 中出现 HTML 标签,并且在 css 选择 器中出现标签名会存在潜在的问题。

2) 使用直接子选择器

很多前端开发人员写选择器链的时候不使用 直接子选择器(注:直接子选择器和后代选择器的
区别)。有时,这可能会导致疼痛的设计问题并且有时候可能会很耗性能。然而,在任何情况下,
这是一个非常不好的做法。如果你不写很通用的,需要匹配到 DOM 末端的选择器, 你应该总 是考虑直接子选择器。

不推荐:

.content .title {
   font-size: 2rem;
  }

推荐:

.content > .title {
   font-size: 2rem;
 }
2.3.3 尽量使用缩写属性

不推荐:

border-top-style: none; 
font-family: palatino, georgia, serif; 
font-size: 100%; line-height: 1.6; 
padding-bottom: 2em; 
padding-left: 1em;
 padding-right: 1em; 
 padding-top: 0;

推荐:

border-top: 0; 
font: 100%/1.6 palatino, georgia, serif; 
padding: 0 1em 2em;
2.3.4 每个选择器及属性独占一行

不推荐:

button { 
 width: 100px; height: 50px;
 color: #fff;
 background: #00a0e9;
  }

推荐:

button {
  width: 100px; 
  height: 50px;
  color: #fff;
  background: #00a0e9; 
}
2.3.5 省略 0 后面的单位

不推荐:

 div {
  padding-bottom: 0px; 
  margin: 0em;
 }

推荐:

div {
    padding-bottom: 0; 
    margin: 0; 
}
2.3.6 避免使用 ID 选择器及全局标签选择器防止污染全局样式

不推荐:

#header {
 padding-bottom: 0px; 
 margin: 0em;
}

推荐:

.header { 
 padding-bottom: 0px; 
 margin: 0em; 
}

2.4 JavaScript规范

2.4.1 命名

请查看上文 1.1.6 JavaScript 文件名

2.4.2 代码格式
2.4.2.1 使用 2 个空格进行缩进

推荐:

if (x < y) {
  x += 10;
} else {
  x += 1; 
}
2.4.2.2 不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开来以 提升可读性

说明:任何情形,没有必要插入多个空行进行隔开。

2.4.3 字符串

统一使用单引号(‘),不使用双引号(“)。这在创建 HTML 字符串非常有好处:
推荐:

 let str = 'foo';
 let testDiv = '<div id="test"></div>'; 

不推荐:

let str = 'foo'; 
let testDiv = "<div id='test'></div>";
2.4.4 对象声明
1) 使用字面值创建对象

推荐:

 let user = {};

不推荐:

 let user = new Object();
2) 使用字面量来代替对象构造器

推荐:

var user = { age: 0, name: 1, city: 3 };

不推荐:

var user = new Object(); 
user.age = 0; 
user.name = 0; 
user.city = 0; 
2.4.5 使用 ES6+

必须优先使用 ES6+ 中新增的语法糖和函数。这将简化你的程序,并让你的代码更加灵活和可复 用。比如箭头函数、await/async , 解构, let , for…of 等等。

2.4.6 括号

下列关键字后必须有大括号(即使代码块的内容只有一行):if, else, for, while, do, switch, try,catch, finally, with。

推荐:

if (condition) { 
   doSomething();
}

不推荐:

if (condition) doSomething();
2.4.7 undefined 判断

永远不要直接使用 undefined 进行变量判断;使用 typeof 和字符串’undefined’对变量进行判断。
推荐:

 if (typeof person === 'undefined') { ... }

不推荐:

if (person === undefined) { ... }
2.4.8 条件判断和循环最多三层

条件判断能使用三目运算符和逻辑运算符解决的,就不要使用条件判断,但是谨记不要写太长的 三目运算符。如果超过 3 层请抽成函数,并写清楚注释。

2.4.9 this 的转换命名

对上下文 this 的引用只能使用 ‘self’ 来命名。

2.4.10 慎用 console.log

因 console.log 大量使用会有性能问题,所以在非 webpack 项目中谨慎使用 log 功能。

注释规范

注释的目的:

  • 提高代码的可读性,从而提高代码的可维护性

注释的原则:

  • 如无必要,勿增注释 ( As short as possible )
  • 如有必要,尽量详尽 ( As long as necessary )

公共组件使用说明

  • api 目录的接口 js 文件必须加注释
  • store 中的 state, mutation, action 等必须加注释
  • vue 文件中的 template 必须加注释,若文件较大添加 start end 注释
  • vue 文件的 methods,每个 method 必须添加注释
  • vue 文件的 data, 非常见单词要加注释

其他

1) 尽量不要手动操作 DOM

因使用 vue 框架,所以在项目开发中尽量使用 vue 的数据驱动更新 DOM,尽量(不到万不得已)不要手动操作 DOM,包括:增删改 dom 元素、以及更改样式、添加事件等。

2) 删除无用代码

因使用了 git/svn 等代码版本工具,对于无用代码必须及时删除,例如:一些调试的 console 语句、无用的弃用功能代码。

扩展

在制定本规范时,除了结合本团队的实际情况,还借鉴了一些大厂和开源组织的前端代码规范:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值