前端开发规范

来源:掘金

Posted by 李一枫 on January 14, 2020

一般规范

命名规范

命名分类

  • camelCase(驼峰式,也叫小驼峰命名,e.g. userInfo
  • PascalCase(帕斯卡命名式,也叫大驼峰命名,e.g. UserInfo
  • kebab-case(短横线连接式,e.g. user-info
  • snake_case(下划线连接式,e.g. user_info

变量命名

  • 命名方式 : 小驼峰式命名方法
  • 命名规范 : 类型+对象描述或属性的方式
// bad
var getTitle = "LoginTable"

// good
let tableTitle = "LoginTable"

函数命名

  • 命名方式 : 小驼峰方式 ( 构造函数使用大驼峰命名法 )
  • 命名规则 : 前缀为动词
动词 含义 返回值
can 判断是否可执行某个动作 (权 ) 函数返回一个布尔值。true:可执行;false:不可执行
has 判断是否含有某个值 函数返回一个布尔值。true:含有此值;false:不含有此值
is 判断是否为某个值 函数返回一个布尔值。true:为某个值;false:不为某个值
get 获取某个值 函数返回一个非布尔值
set 设置某个值 无返回值、返回是否设置成功或者返回链式对象

推荐:

//是否可阅读
function canRead(){
    return true;
}

//获取姓名
function getName{
    return this.name
}

常量命名

  • 命名方法 : 全部大写
  • 命名规范 : 使用大写字母和下划线来组合命名,下划线用以分割单词。

推荐:

const MAX_COUNT = 10;
const URL = 'http://test.host.com';

类的成员命名

  • 公共属性和方法 : 同变量命名方式
  • 私有属性和方法 : 前缀为下划线(_)后面跟公共属性和方法一样的命名方式

推荐:

function Student(name) {
    var _name = name; // 私有成员
 
    // 公共方法
    this.getName = function () {
        return _name;
    }
 
    // 公共方式
    this.setName = function (value) {
        _name = value;
    }
}
var st = new Student('tom');
st.setName('jerry');
console.log(st.getName()); // => jerry:输出_name私有变量的值

注释规范

单行注释

  1. 单独一行://(双斜线)与注释文字之间保留一个空格
  2. 在代码后面添加注释://(双斜线)与代码之间保留一个空格,并且//(双斜线)与注释文字之间保留一个空格。
  3. 注释代码://(双斜线)与代码之间保留一个空格。

推荐 :

// 调用了一个函数 <= 1)单独在一行
setTitle();
 
var maxCount = 10; // 设置最大量 <= 2)在代码后面注释
 
// setName(); // <= 3)注释代码

多行注释 ( / 注释说明 / )

  • 若开始(/\*)和结束(\*/)都在一行,推荐采用单行注释
  • 若至少三行注释时,第一行为/\*,最后行为\*/,其他行以*开始,并且注释文字与*保留一个空格。

推荐 :

/*
* 代码执行到这里后会调用setTitle()函数
* setTitle():设置title的值
*/
setTitle();

函数 ( 方法 ) 注释

函数(方法)注释也是多行注释的一种,但是包含了特殊的注释要求,参照JavaDoc规范

语法:

/** 
* 函数说明 
* @关键字 
*/

常用注释关键字:

注释名 语法 含义 示例
@param @param 参数名 {参数类型} 描述信息 描述参数的信息 @param name {String} 传入名称
@return @return {返回类型} 描述信息 描述返回值的信息 @return {Boolean} true:可执行;false:不可执行
@author @author 作者信息 [附属信息:如邮箱、日期] 描述此函数作者的信息 @author 张三 2015/07/21
@version @version XX.XX.XX 描述此函数的版本号 @version 1.0.3
@example @example 示例代码 @example setTitle(‘测试’)  

需要添加注释的地方

代码注释在一个项目的后期维护中显的尤为重要,所以我们要为每一个被复用的组件编写组件使用说明,为组件中每一个方法编写方法说明。

以下情况,务必添加注释:

1. 公共组件使用说明
2. 各组件中重要函数或者类说明
3. 复杂的业务逻辑处理说明
4. 特殊情况的代码处理说明,对于代码中特殊用途的变量、存在临界值、函数中使用的hack、使用了某种算法或思路等需要进行注释描述
5. 多重 if 判断语句

Vue规范

Vue项目规范

结构化规范(webpack)

   ├── index.html                      入口页面
   ├── favicon.ico                     页面图标
   ├── .babelrc                        babel规则
   ├── .editorconfig                   编辑器配置
   ├── .eslintignore                   eslint忽略规律
   ├── .eslintrc.js                    eslint规则
   ├── .gitignore                      git忽略规则
   ├── build                           构建脚本目录
   │   ├── build-server.js                 运行本地构建服务器,可以访问构后的页面
   │   ├── build.js                        生产环境构建脚本
   │   ├── dev-client.js                   开发服务器热重载脚本,主要用来实现开发阶段的页面自动刷新
   │   ├── dev-server.js                   运行本地开发服务器
   │   ├── utils.js                        构建相关工具方法
   │   ├── webpack.base.conf.js            wabpack基础配置
   │   ├── webpack.dev.conf.js             wabpack开发环境配置
   │   └── webpack.prod.conf.js            wabpack生产环境配置
   │   └── webpack.cdn.conf.js             wabpack cdn配置
   │   └── webpack.dll.conf.js             wabpack dll配置
   ├── config                          项目配置
   │   ├── dev.env.js                      开发环境变量
   │   ├── index.js                        项目配置文件
   │   ├── prod.env.js                     生产环境变量
   │   └── test.env.js                     测试环境变量
   ├── mock                            mock数据目录
   │   └── hello.js
   ├── package.json                    npm包配置文件,里面定义了项目的npm脚本,依赖包等信息
   ├── readmd.md                       项目描述文件
   ├── src                                项目源码目录
   │   ├── main.js                            入口js文件
   │   ├── App.vue                            根组件
   │   ├── components                         公共组件目录
   │   │   └── ComponentItem.vue
   │   ├── assets                         静态资源目录,这里的资源会被wabpack构建
   │   │   ├── css                            公共样式文件目录
   │   │   ├── js                             公共js文件目录(如帮助方法)
   │   │   └── img                            图片存放目录
   |   |── lib                            外部引用的插件存放及修改文件
   |   |—— datas                          模拟数据,临时存放
   │   ├── routes                         前端路由
   │   │   └── index.js
   │   ├── apis                           接口,统一管理
   │   │   └── index.js
   │   ├── store                          vuex, 统一管理
   │   │   └── index.js
   │   └── views                          视图模块名
   │       ├── view-module                视图模块
   |            └── index.vue             视图模块的主页面
   │       ├── hello.vue
   │       └── notfound.vue
   ├── static                             纯静态资源,不会被wabpack构建。
   └── test                               测试文件目录(unit&e2e)
       └── unit                               单元测试
           ├── index.js                           入口脚本
           ├── karma.conf.js                      karma配置文件
           └── specs                           单测case目录
               └── Hello.spec.js

目录、文件、组件命名规范

1. 目录

目录统一使用kebab-case风格

2. views下的文件

  • js类文件使用PascalCase,如UserInfo.js
  • 其他资源文件统一使用kebab-case风格,如user-detail.js, user-detail.css, user-avatar.png

3. 组件文件

  • 命名遵循PascalCase约定。

组件文件名除index.vue之外,一律采用PascalCase(大驼峰)写法。原因是引入组件时,组件的变量通常用PascalCase格式,以区别于一般变量。组件文件名与变量名一致,方便对应。

import UserBook from './user/UserBook'
  • 组件名应该始终是多个单词的,根组件 App 除外

html元素都是单个单词的(如<article>,<header>),为了区分组件和一般html元素,组件由多个单词组成,如BookItem.vue,单独的Book.vue不推荐。

  • 组件使用遵循kebab-case 约定

在页面中使用组件需要前后闭合,并以短线分隔,如:

<user-book></user-book>

Vue开发规范

Vue文件结构

  • 基本结构

顺序:template -> script -> style。一个组件尽量不要超过200行,页面包含独立部分时尽量分离成子组件。

<template>
  <div>...</div>
</template>
<script>
  export default {
    components: {},
    data() {
      return {};
    },
    created(){},
    methods: {},
  };
</script>
<!-- 声明语言,并且添加scoped -->
<style lang="scss" scoped>...</style>
  • 组件/实例的选项顺序
- name          (全局引用)
- components    (模板依赖)
- directives    ...
- filters       ...
- mixins        (组合)
- props         (接口)
- data          (本地状态属性)
- computed      ...
- watch         (响应回调)
- created       (生命周期函数)
- mounted       ...
- methods       (实例属性)

Vue Router Path规范

router path采用kebab-case格式。

用下划线(如:/user_info)或camelCase(如:/userInfo)的单词被当成一个单词,搜索引擎无法区分语义。

// bad
{
    path: '/user_info', // user_info当成一个单词
    name: 'UserInfo',
    component: UserInfo,
    meta: {
    title: ' - 用户',
    desc: ''
    }
},

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

组件开发规范

1. 注册组件

注册组件的时候,全部使用 PascalCase 格式。

import UserBook from './user/UserBook'

2. props 命名规范

  • 在声明prop的时候,其命名应该始终使用camelCase,而在模板中应该始终使用kebab-case
<!-- bad -->
<welcome-message greetingText="hi"></welcome-message>

<script>
  props: {
    'greeting-text': String
  }
</script>

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

<script>
  props: {
    greetingText: String;
  }
</script>
  • prop定义应该尽量详细
    • 组件 props 原子化
    • 提供默认值
    • 使用 type 属性校验类型
// bad 这样做只有开发原型系统时可以接受
props: ['status']

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

3. methods 命名规范

  • 驼峰式命名,统一使用动词或者动词+名词形式
//bad
gonextPageshowloginget_code

// good
jumpPageopenCarInfoDialog
  • 请求数据方法,以 data 结尾
//bad
takeDataconfirmDatagetListpostForm

// good
getListDatapostFormData
  • 尽量使用常用单词开头(set、get、go、can、has、is)

: 函数方法常用的动词:

get 获取/set 设置,
add 增加/remove 删除
create 创建/destory 移除
start 启动/stop 停止
open 打开/close 关闭,
read 读取/write 写入
load 载入/save 保存,
create 创建/destroy 销毁
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 折叠
begin 起始/end 结束,
start 开始/finish 完成
enter 进入/exit 退出,
abort 放弃/quit 离开
obsolete 废弃/depreciate 废旧,
collect 收集/aggregate 聚集

4. 多个属性的html元素规范

多个特性的元素,占据一行过长时,应该分多行撰写,每个特性一行。(增强更易读)

<!-- bad -->
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<my-component foo="fooattribute" bar="barattribute" baz="bazattribute"></my-component>

<!-- good -->
<img
  src="https://vuejs.org/images/logo.png"
  alt="Vue Logo"
>
<my-component
  foo="fooattribute"
  bar="barattribute"
  baz="bazattribute"
>
</my-component>

5. 元素属性的顺序

原生属性放前面,指令放后面

  - class
  - id,ref
  - name
  - data-*
  - src, for, type, href,value,max-length,max,min,pattern
  - title, alt,placeholder
  - aria-*, role
  - required,readonly,disabled
  - is
  - v-for
  - key
  - v-if
  - v-else-if
  - v-else
  - v-show
  - v-cloak
  - v-pre
  - v-once
  - v-model
  - v-bind,:
  - v-on,@
  - v-html
  - v-text

6. 指令规范

  • 指令有缩写则一律采用缩写形式
// bad
v-bind:code="code"
v-on:click="getUserData"

// good
:code="code"
@click="getUserData"
  • v-for 循环必须加上 key 属性,在整个 for 循环中 key 需要唯一
<!-- bad -->
<ul>
    <li v-for="todo in todos">
      
    </li>
</ul>

<!-- good -->
<ul>
    <li v-for="todo in todos" :key="todo.id">
      
    </li>
</ul>
  • 避免 v-ifv-for 同时用在一个元素上(性能问题) 出现这样的需求,有两种解决方案:
  1. 将数据替换为一个计算属性,让其返回过滤后的列表
      <!-- bad -->
  <ul>
    <li v-for="user in users" v-if="user.isActive" :key="user.id">
      
    </li>
  </ul>

  <!-- good -->
  <ul>
    <li v-for="user in activeUsers" :key="user.id">
      
    </li>
  </ul>

  <script>
  computed: {
    activeUsers: function () {
      return this.users.filter(function (user) {
        return user.isActive
      })
    }
  }
  </script>
  1. 将 v-if 移动至容器元素上 (比如 ul, ol)
  <!-- bad -->
  <ul>
    <li v-for="user in users" v-if="shouldShowUsers" :key="user.id">
      
    </li>
  </ul>

  <!-- good -->
  <ul v-if="shouldShowUsers">
    <li v-for="user in users" :key="user.id">
      
    </li>
  </ul>

其他

  1. 避免 this.$parent
  2. 调试信息 console.log() 使用完及时删除
  3. 除了三目运算,if, else 等禁止简写
  4. 行尾分号可以加或不加,应统一风格,但建议都不加。分号的作用在现代ES脚本不会影响,如果你不懂分号的作用,该入的坑照样会入,没有分号的代码更加清爽。大部分Vue项目都去除了分号。
// bad
if (true)
  alert(name);
console.log(name);

// bad
if (true)
alert(name);
console.log(name);

// good
if (true) {
  alert(name)
}
console.log(name)

HTML规范

语义化

语义化是指:根据元素其被创造出来时的初始意义来使用它。 用正确的标签表达正确的内容,这也有利于SEO。

alt标签不为空

<img> 标签的 alt 属性指定了替代文本,用于在图像无法显示或者用户禁用图像显示时,代替图像显示在浏览器中的内容。 假设由于下列原因用户无法查看图像,alt 属性可以为图像提供替代的信息:

  • 网速太慢
  • src 属性中的错误
  • 浏览器禁用图像
  • 用户使用的是屏幕阅读器 从SEO角度考虑,浏览器的爬虫爬不到图片的内容,所以我们要有文字告诉爬虫图片的内容

结构、表现、行为三者分离

  • 尽量在文档和模板中只包含结构性的 HTML;
  • 将所有表现代码,移入样式表中;
  • 将所有动作行为,移入脚本之中。

HTML只关注内容

  • HTML只显示展示内容信息
  • 不要引入一些特定的 HTML 结构来解决一些视觉设计问题,多考虑使用伪元素:before:after
  • 不要将 img 元素当做专门用来做视觉设计的元素
  • 样式上的问题应该使用css解决
<!-- bad -->
<!-- We should not introduce an additional element just to solve a design problem  -->
<span class="text-box">
  <span class="square"></span>
  See the square next to me?
</span>

css 代码:

.text-box > .square {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  background-color: red;
}
<!-- good -->
<!-- That's clean markup! -->
<span class="text-box">
  See the square next to me?
</span>

css 代码:

/* We use a :before pseudo element to solve the design problem of placing a colored square in front of the text content */
.text-box:before {
  content: "";
  display: inline-block;
  width: 1rem;
  height: 1rem;
  background-color: red;
}

JS规范

一般规范

使用严格等===

总是使用 === 精确的比较操作符,避免在判断的过程中,由 JavaScript 的强制类型转换所造成的困扰。

this关键字

只在对象构造器、方法和在设定的闭包中使用 this 关键字。

this 的语义常容易被误导。它时而指向全局对象(大多数时),时而指向调用者的定义域(在 eval 中),时而指向 DOM 树中的某一节点(当用事件处理绑定到 HTML 属性上时),时而指向一个新创建的对象(在构造器中),还时而指向其它的一些对象(如果函数被 call() 和 apply() 执行和调用时)。

正因为它是如此容易地被搞错,请限制它的使用场景:

  • 在构造函数中
  • 在对象的方法中(包括由此创建出的闭包内)

三元操作符

用三元操作符分配或返回语句。在比较简单的情况下使用,避免在复杂的情况下使用。

// bad
if(x === 10) {
  return 'valid';
} else {
  return 'invalid';
}

// good
return x === 10 ? 'valid' : 'invalid'

不使用eval()函数

就如eval的字面意思来说,恶魔,使用eval()函数会带来安全隐患。 eval()函数的作用是返回任意字符串,当作js代码来处理。

使用ES6编码规范

  • 定义变量使用let ,定义常量使用const
  • 静态字符串一律使用单引号或反引号,动态字符串使用反引号
// bad
const a = 'foobar';
const b = 'foo' + a + 'bar';

// good
const a = 'foobar';
const b = `foo${a}bar`;
const c = 'foobar';

  • 解构赋值
// 1. 数组解构赋值
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;

// 2. 对象解构赋值
// bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;
}
// good
function getFullName(obj) {
  const { firstName, lastName } = obj;
}
// best
function getFullName({ firstName, lastName }) {}
  • 使用扩展运算符(…)拷贝数组。
const items = [1, 2, 3, 4, 5];
// bad
const itemsCopy = items;
// good
const itemsCopy = [...items];
  • 箭头函数 需要使用函数表达式的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了 this
// bad
const self = this;
const boundMethod = function(...params) {
  return method.apply(self, params);
};
// good
const boundMethod = (...params) => method.apply(this, params);

CSS规范

合理地使用ID

一般情况下ID不应该被用于样式,并且ID的权重很高,所以不使用ID解决样式的问题,而是使用class

css选择器中避免使用标签名

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

使用直接子选择器

使用后代选择器常可能导致设计问题和性能损耗,这是个不好的做法,应总是考虑直接子选择器

/* bad */
.content .title {
  font-size: 2rem;
}
/* good */
.content > .title {
  font-size: 2rem;
}

尽量使用缩写属性

尽量使用缩写属性对于代码效率和可读性是很有用的,比如font属性。

/* bad */
.user-box {
    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;
}

/* good */
.user-box {
   border-top: 0;
   font: 100%/1.6 palatino, georgia, serif;
   padding: 0 1em 2em; 
}

0后面不带单位

/* bad */
.user-box {
    padding-bottom: 0px;
    margin: 0em;
}

/* good */
.user-box {
    padding-bottom: 0;
    margin: 0;
}

属性格式

  • 为了保证一致性和可扩展性,每个声明应该用分号结束,每个声明换行。
  • 属性名的冒号后使用一个空格。出于一致性的原因,
  • 属性和值(但属性和冒号之间没有空格)的之间始终使用一个空格。
  • 每个选择器和属性声明总是使用新的一行。
  • 属性选择器或属性值用双引号(””),而不是单引号(”)括起来。

属性顺序

作为最佳实践,我们应该遵循以下顺序:

  • 结构性属性: display position, left, top, right etc. overflow, float, clear etc. margin, padding
  • 表现性属性: background, border etc. font, text
/* bad */
 .box {
  font-family: 'Arial', sans-serif;
  border: 3px solid #ddd;
  left: 30%;
  position: absolute;
  text-transform: uppercase;
  background-color: #eee;
  right: 30%;
  isplay: block;
  font-size: 1.5rem;
  overflow: hidden;
  padding: 1em;
  margin: 1em;
}

/* good */
.box {
  display: block;
  position: absolute;
  left: 30%;
  right: 30%;
  overflow: hidden;
  margin: 1em;
  padding: 1em;
  background-color: #eee;
  border: 3px solid #ddd;
  font-family: 'Arial', sans-serif;
  font-size: 1.5rem;
  text-transform: uppercase;
}

作者:李一枫 链接:https://juejin.im/post/5e1abeede51d453c913c340e 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。