为提高团队协作效率, 便于后台人员添加功能及前端后期优化维护,输出高质量的文档,特制订此文档。 本规范文档一经确认, 前端开发人员须按本文档规范进行开发。 本文档如有不对或者不合适的地方请及时提出,经讨论决定后可以更改此文档。
1、基本原则
符合 web 标准 ( UTF-8 HTML5 ) ,语义化 html ( HTML5 新增要求,减少 div 和 span 等无特定语义的标签使用 ) ,结构表现行为分离 ( HTML-CSS-JS代码分离,不同行为代码高内聚低耦合 ) ,兼容性优良 ( 早期版本浏览器兼容,移动端和PC端设备兼容 ) 。页面性能方面 ( 减少请求次数,例如使用精灵图和sass语法 ) ,代码要求简洁明了有序,尽可能的减小服务器负载,保证最快的解析速度 ( 减小 repaint 和 reflow ) 。禁止直接操作DOM。
2、文件命名、结构规范
- 文件名不能含有空格
- 文件名建议只使用小写字母,不使用大写字母。( 但比如说像 github 的说明类文件,README,则应该全部使用大写 )
- 文件名包含多个单词时,单词之间建议使用半角的连词线 ( - ) 分隔。
3、iconfont--使用字体图标减少图片资源消耗
4、JS 书写规范 - 遵照ES6标准
- 命名规范
let 定义的变量不会被变量提升,const 定义的常量不能被修改,let 和 const 都是块级作用域
尽量不实用 var
const 定义常量全部大写并单词间用下划线分隔
小驼峰式定义普通变量比如对象的属性或方法名、变量名、函数名
小驼峰式但首字母大写定义类名 ( 构造器 )
小驼峰式但需要用_开头定义私有变量
// const 定义常量
export const VOUCHER_STATUS_NORMAL = 1 // 正常
export const VOUCHER_STATUS_ADD = 2 // 新增
export const VOUCHER_STATUS_EDIT = 3 // 编辑 (查看状态下进行了修改)
// 对象的属性或方法名、变量名、函数名
pageChanged = (pageIndex, pageSize) => {
let page = this.metaAction.gf('data.pagination').toJS(),
filter = this.metaAction.gf('data.filter').toJS()
if(pageIndex){
page.pageIndex = pageIndex
}
if(pageSize){
page.pageSize = pageSize
}
this.load( page, filter)
}
// 定义类名 ( 构造器 )
class DragTitle extends Component {
updateTransform = transformStr => {
this.modalDom.style.transform = transformStr;
};
componentDidMount() { ...
...
// 定义私有变量
let _current, _defaultConfig
- import导入模块、export导出模块
// 全部导入
import people from './example'
// 将整个模块当作单一对象进行导入,该模块的所有导出都会作为对象的属性存在
import * as example from "./example.js"
console.log(example.name)
console.log(example.getName())
// 导入部分,引入非 default 时,使用花括号
import {name, age} from './example'
// 导出默认, 有且只有一个默认
export default App
// 部分导出
export class App extend Component {};
- class、extends、super
ES5中最令人头疼的的几个部分:原型、构造函数,继承,ES6 引入了 Class ( 类 ) 这个概念。
ES6 允许我们用 class 定义一个“类”。
Class 之间可以通过 extends 关键字实现继承。
super 关键字,它指代父类的实例 ( 即父类的 this 对象 )。子类必须在 constructor 方法中调用 super 方法,否则就得不到 this 对象。
更多 ES6 类声明继承机制详见 ES6 类 共同学习共同进步
class Animal {
constructor() {
this.type = 'animal';
}
says(say) {
console.log(this.type + ' says ' + say);
}
}
let animal = new Animal();
animal.says('hello'); //animal says hello
class Cat extends Animal {
constructor() {
super();
this.type = 'cat';
}
}
let cat = new Cat();
cat.says('hello'); //cat says hello
- arrow functions ( 箭头函数 )
函数的快捷写法。不需要 function 关键字来创建函数,省略 return 关键字,继承当前上下文的 this 关键字
箭头函数小细节:当你的函数有且仅有一个参数的时候,是可以省略掉括号的;当你函数中有且仅有一个表达式的时候可以省略{}
JavaScript 语言的 this 对象一直是一个令人头痛的问题,运行上面的代码会报错,这是因为 setTimeout 中的 this 指向的是全局对象。
当我们使用箭头函数时,函数体内的 this 对象,就是定义时所在的对象
更多 ES6 箭头函数 this 巧用详见 ES6写法优化 箭头函数 共同学习共同进步
// ES5
var arr1 = [1, 2, 3];
var newArr1 = arr1.map(function(x) {
return x + 1;
});
// ES6
let arr2 = [1, 2, 3];
let newArr2 = arr2.map((x) => {
x + 1
});
let arr2 = [1, 2, 3];
let newArr2 = arr2.map(x => x + 1);
- template string ( 模板字符串 )
字符串拼接。将表达式嵌入字符串中进行拼接,用和${}
来界定。
在ES5时我们通过反斜杠来做多行字符串拼接。ES6反引号 `` 直接搞定。
更多 ES6 标签模版字符串用法详见 ES6写法优化 字符串模板 共同学习共同进步
// es5
var name1 = "bai";
console.log('hello' + name1);
// es6
const name2 = "ming";
console.log(`hello${name2}`)
// es5
var msg = "Hi \
man!";
// es6
const template = `
hello world
`;
// includes:判断是否包含然后直接返回布尔值
let str = 'hahah';
console.log(str.includes('y')); // false
// repeat: 获取字符串重复n次
let s = 'he';
console.log(s.repeat(3)); // 'hehehe'
- destructuring ( 解构 )
解构能让我们从对象或者数组里取出数据存为变量以数组为例
更多 ES6 解构用法详见 ES6 对象、数组解构 共同学习共同进步
const numbers = [ 'one', 'two', 'three', 'four' ];
const [one, , three, , five = 'have no five'] = numbers; // 相对应位置的值 默认值写法同对象一样用=
console.log(one, three, five); // one three have no five
- Promise 用同步的方式去写异步代码
更多 ES6 Promise 用法详见 ES6 Promise 共同学习共同进步
// 发起异步请求
fetch('/api/todos')
.then(res => res.json())
.then(data => ({
data
}))
.catch(err => ({
err
}));
- ES6 Generator 生成器控制 ajax 工作流
用 Generator 生成器来达到 Promise 的效果,控制 ajax 工作流,代码有更好的可读性,从上到下,一气呵成。
- 使用严格的条件判断符
用 === 代替 == ,用 !== 代替 != ,避免在条件判断时掉入 == 造成的陷阱
// 这样的一些值表示 false 。
null
undefined 与 null 相等
字符串 ''
数字 0
NaN
(function () {
var undefined;
undefined == null; // true
1 == true; //true
2 == true; // false
0 == false; // true
0 == ''; // true
NaN == NaN;// false
[] == false; // true
[] == ![]; // true
})();
5、关于VUE的开发规范
- 数据管理优先使用 Vuex ,通用组件选择性使用 props 方式
- store 的定义可以以组件的维度做好分解,在入口处做好聚合
- api 接口模块管理--统一管理 api 请求
- vue 生命钩子
- vue文件基本结构
...
...
- vue文件方法声明顺序
1.副作用 (触发组件外的影响)
--el
2.全局感知 (要求组件以外的知识)
--name
--parent
3.组件类型 (更改组件的类型)
--functional
4.模板修改器 (改变模板的编译方式)
--delimiters
--comments
5.模板依赖 (模板内使用的资源)
--components
--directives
--filters
6.组合 (向选项里合并属性)
--extends
--mixins
7.接口 (组件的接口)
--inheritAttrs
--model
--props/propsData
8.本地状态 (本地的响应式属性)
--data
--computed
9.事件 (通过响应式事件触发的回调)
--watch
生命钩子函数
---beforeCreate
---created
---beforeMount
---mounted
---beforeUpdate
---updated
---activated
---deactivated
---beforeDestroy
---destroyed
10.非响应式的属性 (不依赖响应系统的实例属性)
--methods
11.渲染 (组件输出的声明式描述)
--template/render
--renderError
- 元素特性的顺序
- v-for
- v-if / v-show
- id
- ref / key / slot
- v-model
- v-on//强烈建议用简写 @,如@change
- 单文件组件的顶级元素的顺序
单文件组件应该总是让- 多个特性的元素
多个特性的元素应该分多行撰写,每个特性一行。
- 模板中简单的表达式
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。
复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
{{ normalizedFullName }} // 复杂表达式已经移入一个计算属性 computed: { normalizedFullName: function () { return this.fullName.split(' ').map(function (word) { return word[0].toUpperCase() + word.slice(1) }).join(' ') } }
- 简单的计算属性
computed: { basePrice: function() { return this.manufactureCost / (1 - this.profitMargin) }, discount: function() { return this.basePrice * (this.discountPercent || 0) }, finalPrice: function() { return this.basePrice - this.discount } }
- 谨慎使用 ( 有潜在危险的模式 )
// 如果一组 v-if + v-else 的元素类型相同,最好使用 key (比如两个
元素)。错误:{{ error }}{{ results }}// 元素选择器应该避免在 scoped 中出现。在 scoped 样式中,类选择器比元素选择器更好,因为大量使用元素选择器是很慢的。 // 隐性的父子组件通信 应该优先通过 prop 和事件进行父子组件之间的通信,而不是 this.$parent 或改变 prop。 Vue.component('TodoItem', { props: { todo: { type: Object, required: true } }, template: ` ` }) // 非 Flux 的全局状态管理 应该优先通过 Vuex 管理全局状态,而不是通过 this.$root 或一个全局事件总线。 // store/modules/todos.js export default { state: { list: [] }, mutations: { REMOVE_TODO (state, todoId) { state.list = state.list.filter(todo => todo.id !== todoId) } }, actions: { removeTodo ({ commit, state }, todo) { commit('REMOVE_TODO', todo.id) } } } {{ todo.text }}6、CSS 书写规范
- webApp 自适应布局 -- 使用 rem 。
使用 rem 单位可以让我们的设计更加灵活,能够控制元素整体放大缩小,而不是固定大小。 我们可以使用这种灵活性,使我们在开发期间,能更加快速灵活的调整,允许浏览器用户调整浏览器大小来达到最佳体验。 - class 与 id 的使用:id 是唯一的并是父级的,class 是可以重复的并是子级的,所以 id 仅使用在大的模块上,class 可用在重复使用率高及子级中。
- class 命名,体现嵌套统一使用 - 来连接,参考 TTK 框架 企业开发平台,每一个app的最外层与文件夹保持一致,header、content、footer 依次排列层级分明
避免使用通配规则和相邻兄弟选择符、子选择符,、后代选择符、属性选择符等选择器
不要限定 id 选择符,如 div#header(提权的除外)
不要限定类选择器,如 ul.recommend(提权的除外)
不要使用 ul li a 这样长的选择符
避免使用标签子选择符,如 #header > li > a
// dom 结构对应 css className name: 'root', component: 'Layout', className: 'open-portal-config-project-list', // app 名称 children: [{ name: 'header', component: 'Layout', className: 'open-portal-config-project-list-header', ... name: 'content', component: 'Layout', className: 'open-portal-config-project-list-content', children: [{ ... name: 'footer', component: 'Layout', _visible: false, className: 'open-portal-config-project-list-footer', ... // 对应 less 文件结构 .open-portal-config-project-list{ align-items: center; margin: 0 auto; width: 100%; height: 100%; background-color: #fff; &-header{ // 尽量减少样式 class 嵌套 方便解析查找以及日后修改 width: 100%; flex: initial; flex-direction: row; justify-content: space-between; line-height: 40px; padding: 10px; .mk-input { width: 200px; margin-right: 10px; } } &-content { flex: 1; width: 100%; } &-footer { flex: initial; width: 100%; padding: 10px; .mk-pagination { justify-content: flex-end; } } }
- 书写代码前,提高样式重复使用率。充分利用 html 自身属性及样式继承原理减少代码量。
- 样式表中中文字体名,请务必转码成 unicode 码,以避免编码错误时乱码。
- 减少使用影响性能的属性,比如 position:absolute || float。
- 兼容多个浏览器时,将标准属性写在底部。
-moz-border-radius: 15px; /* Firefox */ -webkit-border-radius: 15px; /* Safari和Chrome */ border-radius: 15px; /* Opera 10.5+, 以及使用了IE-CSS3的IE浏览器 */ //标准属性
- 尽量避免使用CSS Hack。
property:value; /* 所有浏览器 */ +property:value; /* IE7 */ _property:value; /* IE6 */ *property:value; /* IE6/7 */ property:value\9; /* IE6/7/8/9,即所有IE浏览器 */
7、注释规范
- 单行注释
单独一行:// ( 双斜线 ) 与注释文字之间保留一个空格
在代码后面添加注释:// ( 双斜线 ) 与代码之间保留一个空格,并且 // ( 双斜线 ) 与注释文字之间保留一个空格。
注释代码:// ( 双斜线 ) 与代码之间保留一个空格。
// 调用了一个函数;1)单独在一行 setTitle(); var maxCount = 10; // 设置最大量;2)在代码后面注释 // setName(); // 3)注释代码
- 多行注释
若开始和结束都在一行,推荐采用单行注释
若至少三行注释时,第一行为 / ,最后行为 / ,其他行以开始,并且注释文字与 * 保留一个空格。
/* * 代码执行到这里后会调用setTitle()函数 * setTitle():设置title的值 */ setTitle();
- 函数 ( 方法 ) 注释
/** * 函数说明 * @关键字 */
8、HTML 书写规范
- 文档类型声明及编码:统一为 html5 声明类型。书写时利用 IDE 实现层次分明的缩进 ( 默认缩进4空格 ) 。
- 非特殊情况下 CSS 文件放在 body 部分 标签后。非特殊情况下大部分 JS 文件放在 标签尾部 ( 如果需要界面未加载前执行的代码可以放在 head 标签后 ) 避免行内 JS 和 CSS 代码。
- 所有编码需要遵循 html ( XML ) 标准,标签 & 属性 & 属性命名必须由小写字母及下划线数字组成,且所有标签必须闭合,包括 br(),hr() 等。属性值用双引号。应当按照以下给出的顺序依次排列,来确保代码的易读性。
class id 、 name data-* src、for、 type、 href title、alt aria-*、 role
- 引入 JS 库文件,文件名须包含库名称及版本号及是否为压缩版,比如 jquery-1.4.1.min.js。引入插件,文件名格式为库名称 + 插件名称,比如 jQuery.bootstrap.js。
jQuery-1.8.3.min.js
- 书写页面过程中,请考虑向后扩展性。class & id 参见 css 书写规范。
- 语义化 html,如标题根据重要性用 h* ( 同一页面只能有一个 h1 ) ,段落标记用 p ,列表用 ul ,内联元素中不可嵌套块级元素。
- 尽可能减少 div 多层级嵌套。
- 书写链接地址时,必须避免重定向,例如:href="https://github.com/thethreekingdoms/
",即须在 URL 地址后面加上 “ / ” 。 - 在页面中尽量避免使用 style 属性,即 style = " … " 。
- 能以背景形式呈现的图片,尽量写入 css 样式中。
- 重要图片必须加上 alt 属性。给重要的元素和截断的元素加上 title 。
- 给区块代码及重要功能 ( 比如循环 ) 加上注释,方便后台添加功能。
- 特殊符号使用:尽可能使用代码替代:比如 <(<)&>(>)& 空格 ()&»(») 等等。
- 语义化 html ( HTML5 新增要求,减少 div 和 span 等无特定语义的标签使用 )
- 多个特性的元素