前端规范

对于不同的企业有不同的前端(代码/文件目录/打包发布)规范,其下为个人习得或习惯。

代码规范

HTML

文档统一采用UTF-8编写(设置IDE)

代码缩进采用4个空格

一个标签多个属性(导致折行),每个属性单独成行

1
2
3
4
5
6
7
8
9
<div 
data-prop1="1"
data-prop2="2"
data-prop3="3"
data-prop4="4"
...
>
<!-- ... -->
</div>

注释的使用

  1. 用于模块开始和结束
1
2
3
<!-- 模块名 -->
<div class="module"></div>
<!-- /模块名 -->
  1. 用于说明
1
2
<!-- 这是一个组件 -->
<div class="component"></div>
  1. 暂存代码(可能下次需要)

外链样式文件放在head标签之中, 外链JS文件放在body结束标签之前

1
2
3
4
5
6
7
8
9
<!-- ... -->
<head>
<link href="./style.css" rel="stylesheet">
</head>
<body>
<!-- ... -->
<script src="./index.js"></script>
</body>
<!-- ... -->

使用标签语意化

1
2
3
<header></header>
<footer></footer>
<!-- ... -->

标签正确嵌套

  1. 两个标签不能交叉嵌套
1
2
3
4
// Error
<span>
<div></span>
</div>
  1. 一般情况下,行内元素不能嵌套块级元素
1
2
3
4
// BAD
<span>
<div></div>
</span>

遵照HTML5声明文档类型

1
<!DOCTYPE html>

元信息设置

1
2
3
4
5
<meta charset="UTF-8">
<!-- 根据实际情况调整 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- 其他有利于SEO信息,如: keywords,description等 -->

标签及属性名均采用小写,且属性值采用双引号(“”),自定义属性以data-开头

1
2
3
4
<div
class="module"
data-prop-name="<propName>"
></div>

CSS/LESS/SASS等

css代码位置

一般情况下css代码放置于单独文件,外部链接进html文件,特殊情况(页面简单等)下也可将其放入style标签,正常情况下不应在标签上设置样式。
在css中不使用@import,除开LESS/SASS文件中。

样式的写法

本人一般采用OO结合BEM的写法,OO定义基本的原子类,便于组合,BEM具有一定的模块化封装性。

id与class定义

  • id采用驼峰形式,如:#fullName
  • class采用小写字母加中划线(-)或下划线(_),详情说明参考BEM,如:.btn--primary, .user__avatar--small
  • JS中使用class,以js-开头

样式属性顺序:尺寸->形状->内填充/外边距->文字->位置等(不严格按照这顺序)

注释

  1. 大的模块/分段
1
2
3
4
5
6
7
8
9
10
/**
* ==================================
* 模块名称, 如:原子类/重置类等
* 描述信息
* ==================================
*/

.mb15 {
margin-bottom: 15px;
}
  1. 样式属性说明
1
2
3
4
5
/* 说明 */
.className {
/* 说明 */
color: #ff0000;
}

属性值缩写,如:颜色,font,background等

样式换行

  • 不同属性单独成行
1
2
3
4
5
6
7
8
/* 不推荐 */
.className {font-size: 12px;color: #ff0000;}

/* 推荐 */
.className {
font-size: 14px;
color: #ff0000;
}
  • 多个class共同样式,不同类名单独成行
1
2
3
4
5
6
7
8
9
/* 不推荐 */
.className1,.className2 {
/* ... */
}
/* 推荐 */
.className1,
.className2 {
/* ... */
}

JavaScript

基础规范

使用es6+相关语法,同时polyfill,最后采用babel转码。

  • 变量定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义变量用let,常量用const

// 变量,驼峰命名法
let fullName = 'Jacky';

// 常量
const API_ROOT = '';

// 多个变量,多个let
let var1 = '';
let var2 = '';

// 不推荐
let var1 = '',
var2 = '';
  • 构造函数首字母大写
1
2
3
4
5
6
7
function Person () {
// ...
}

class Person {
// ...
}
  • 空格使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 运算符前后加入空格
let a = 1;
let b = 2;
let c = a + b;

// 数组项之间
let arr = ['item1', 'item2', 'item3'];

// 对象字面量key/value间
let obj = {
prop1: 'prop1',
prop2: 'prop2',
}

// 函数名与括号间,参数间,函数体左括号与参数右括号间
function ask (name, content) {
// ...
}

// 条件
if (age === 20) {
// ...
}

// 循环
for (let i = 0; i < 10; i++) {
// ...
}
  • 采用2个空格进行代码缩进
1
2
3
4
5
6
7
8
9
// 不推荐
if (space === 4) {
alert('4个空格');
}

// 推荐
if (space === 2) {
alert('2个空格');
}
  • 语句结束必须使用分号(;)
1
2
3
4
5
// 不推荐
let num1 = 1

// 推荐
let num1 = 1;
  • 箭头函数括号使用
1
2
3
4
5
6
7
// 不推荐
let func1 = (a, b) => a + b;
let func2 = a => a + 1;

// 推荐
let func1 = (a, b) => (a + b);
let func2 = (a) => (a + 1);
  • 注释的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* 块级注释 */

/**
* 函数/方法注释
* @param {String} fullName
* @return {String}
*/
function sayHello (fullName) {
// ...
}

// 行注释
let internal = null; // 变量
let obj = {
// 属性说明
prop1: ''
};

/******************************
* 功能块分隔
******************************/
  • 避免使用过多的全局变量
1
2
3
4
5
6
7
8
9
// 采用命名空间(namespace)
let utils = {};
utils.date = {};
utils.currency = {};

// 采用IFFF(自执行函数)包裹代码块
(function () {
// ...
})();

库/框架

jQuery

  • jQuery对象变量名称以$开头
1
let $fullName = $('#fullName')
  • 多次使用的jQuery对象变量缓存
  • 查询元素,缩小范围,减少选择器复杂度通过find
1
2
3
4
5
// 不推荐
$('form input');

// 推荐
$('form').find('.input');
  • 方法链式调用
1
2
3
4
5
$('.blk')
.html('hello')
.css({
'background-color': '#ff0000'
});
  • css操作用class,不直接操作属性
1
2
3
.active {
color: #ff0000;
}
1
2
3
4
5
6
// 不推荐
$('.blk').css({
color: '#ff0000'
});
// 推荐
$('.blk').addClass('active');
  • 绑定列表元素事件采用事件委托
1
2
3
$('.list').on('click', '.list-item', function () {
// ...
});
  • 减少DOM操作,使用字符串拼接(join),然后一次性append
1
2
3
4
5
6
7
8
let $list = $('.list');
let listItemStrs = [];
for (let i = 0; i < 10; i++) {
listItemStrs.push(`<li>${i}</li>`);
// 不推荐
// $list.append(`<li>${i}</li>`);
}
$list.append(listItemStrs.join(''));
  • ajax回调采用Promise
1
2
3
4
5
6
7
$.ajax({
...
}).then(function () {
// success callback
}, function () {
// fail callback
});

VUE

参考文档

以上html规范适用于VUE组件html模版定义,JS规范适用于组件JS定义,CSS适用于组件样式定义。

  • 用vue-cli脚手架搭建初始化项目
  • 组件书写采用单文件
  • 组件粒度按照实际情况调整,如:公共组件小粒度,便于复用;复用性不大的,可以大粒度。
  • 组件名称采用小写字母加中划线(-)命名
  • 组件props定义,需定义类型及默认值
1
2
3
4
5
6
7
8
9
10
export default {
props: {
max: {
type: Number,
default () {
return 10;
}
}
}
}
  • 合理使用computed/watch/methods,详细原因参考:计算属性和侦听器
  • 组件的样式均采用scoped,有助于提高组件的封闭性
1
2
3
<style lang="scss" scoped>
/* ... */
</style>

React

  • 用create-react-app作为项目的脚手架,初始化搭建项目
  • 组件书写采用单文件
  • 为src目录增加resolve.alias,修改webpack配置,以方便文件引用
1
2
3
4
5
6
7
8
// webpack config
{
resolve: {
alias: {
'@': path.resolve(__dirname, '../src'),
}
}
}
  • 采用ES6及JSX编写组件,以提高效率/可读性/可维护性
  • 组件名及文件名采用帕斯卡命名法(首字母大写,大驼峰)形式,如:MyComponent.js
  • 组件代码首先引入node_modules中模块,然后再引入自定义组件,其次在引入样式及图片文件
1
2
3
4
5
import React, { Component } from 'react';
import { List } from 'antd';
import Banner from './Banner';
import styles from './style.less';
// ...
  • 使用装饰器
1
2
3
@withRouter
export default class MyComponent extends Component {
}
  • 按照需要详细定义组件propTypes及defaultProps
1
2
3
4
5
6
7
8
9
export default class MyComponent extends Component {
static propTypes = {
max: PropTypes.number,
}

static defaultProps = {
max: 10,
}
}
  • 组件/标签属性较多时,每条属性单独成行
  • 合理使用组件命名空间,如:List.Item
  • JSX用()包裹
  • 按照生命周期组顺序织组件的方法、属性
  • 组件中样式尽量少使用css-in-js形式,将样式提取到单独文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 不推荐
// ...
export default class Com extends Component {
render () {
const style = {
backgroundColor: '#ff0000',
};
return (
<div style={style}></div>
)
}
}

// 推荐
// ...
import styles from './style.less';
export default class Com extends Component {
render () {
return (
<div className={styles.root}></div>
)
}
}
  • 组件中事件监听函数采用箭头函数绑定上下文
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default class Com extends Component {
// 不推荐
// constructor (props) {
// super(props);
// this.handleClick = this.handleClick.bind(this);
// }

// handleCLick () {
// // ...
// }

// 推荐
handleClick = () => {
// ...
}

render () {
return (
<div onClick={this.handleClick}></div>
)
}
}

文件目录规范

SPA (单页应用)

project_dir
  src
    assets
      images
      styles
      ...
    components
      Banner
        index.js
    utils
      date.js
      currency.js
      ...
    pages
      order
        Detail.js
        List.js
        ...
    index.js

MPA (多页应用)

project_dir
  src
    assets
      images
      styles
      scripts
      ...
    pages
      page1.js
      page2.js
      ...
    tpls
      page1.html
      page2.html
      ...
```

打包发布

基于现在情况,本公司现在打包基本上采用webpack及相关插件予以实现,采用前端针对开发和生产环境分别打包并生成zip文件(存放在git中)交由后端开发人员部署。

需用到以下工具:

  • node packages:cross-env
  • webpack plugins: webpack.definePlugin/zip-webpack-plugin

当然可以采用jenkins优化发布过程。可以参见《Jenkins+Github持续集成》