Vue代码规范
认识代码规范
先来思考两个问题:
- 什么是代码规范?
- 为什么需要代码规范?
如果你是一个经验丰富的前端开发,你一定接触过这样的老项目:变量名是 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组件的命名格式?
- PascalCase 是合法的 JavaScript 标识符。这使得在 JavaScript 中导入和注册组件都很容易,同时 IDE 也能提供较好的自动补全。
<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-if
和 v-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 语句、无用的弃用功能代码。
扩展
在制定本规范时,除了结合本团队的实际情况,还借鉴了一些大厂和开源组织的前端代码规范: