点赞在看,养成习惯
正文共: 23323 字 ------- 预计阅读时间: 59 分钟
注:分享出来跟大家交流,也希望能在留言区探讨,得以改进。
为实现代码一致性和最佳实践,通过代码风格的一致性,降低维护代码的成本以及改善多人协作的效率。同时遵守最佳实践,确保页面性能得到最佳优化和高效的代码。
一、通用书写规范
1. 基本原则:结构、样式、行为分离
- 尽量确保文档和模板只包含 HTML 结构,样式都放到 css 样式表里,行为都放到 js 脚本里
- 标记应该是结构良好、语义正确。
- Javascript应该起到渐进式增强用户体验的作用 。
2.文件/资源命名
在 web 项目中,使用连字符(-)来分隔文件名,可以提高可读性。例如:order-detail-view.js。确保不用大写字母开头,不要驼峰命名。
3.省略外链资源 URL 协议部分
省略外链资源(图片及其它媒体资源)URL 中的 http / https 协议,使 URL 成为相对地址,避免Mixed Content 问题。
4.写注释
写注释时请一定要注意:写明代码的作用,重要的地方一定记得写注释。 没必要每份代码都描述的很充分,它会增重HTML和CSS的代码。这取决于该项目的复杂程度。
4.1 单行注释说明:
单行注释以两个斜线开始,以行尾结束
// 调用了一个函数
setTitle();
var maxCount = 10; // 设置最大量
4.2 多行注释
/*
* 代码执行到这里后会调用setTitle()函数
* setTitle():设置title的值
*/
4.3 函数注释
/**
* 以星号开头,紧跟一个空格,第一行为函数说明
* @param {类型} 参数 单独类型的参数
* @param {[类型|类型|类型]} 参数 多种类型的参数
* @param {类型} [可选参数] 参数 可选参数用[]包起来
* @return {类型} 说明
* @author 作者 创建时间 修改时间(短日期)改别人代码要留名
* @example 举例(如果需要)
*/
4.4 文件头注释
推荐:VScode 文件头部自动生成注释插件:koroFileHeader
4.5 条件注释
<!--[if IE 9]>
.... some HTML here ....
<![endif]-->
二.HTML规范
1.通用约定
标签
- 自闭合(self-closing)标签,无需闭合 ( 例如:
img
input
br
hr
等 ); - 可选的闭合标签(closing tag),需闭合 ( 例如:
</li>
或</body>
); - 尽量减少标签数量;
例:
- class 应以功能或内容命名,不以表现形式命名;
- class 与 id 单词字母小写,多个单词组成时,采用中划线-分隔;
- 使用唯一的 id 作为 Javascript hook, 同时避免创建无样式信息的 class;
<!-- Not recommended -->
<div class="item-hook left contentWrapper"></div>
<!-- Recommended -->
<div id="item-hook" class="sidebar content-wrapper"></div>
属性顺序 HTML 属性应该按照特定的顺序出现以保证易读性。
- id
- class
- name
- data-xxx
- src, for, type, href
- title, alt
- aria-xxx, role
<a id="..." class="..." data-modal="toggle" href="###"></a>
<input class="form-control" type="text">
<img src="..." alt="...">
嵌套
<a>
不允许嵌套 <div>
这种约束属于语义嵌套约束,与之区别的约束还有严格嵌套约束,比如<a>
不允许嵌套 <a>
。
严格嵌套约束在所有的浏览器下都不被允许;而语义嵌套约束,浏览器大多会容错处理,生成的文档树可能相互不太一样。
语义嵌套约束
<li>
用于<ul>
或<ol>
下;<dd>
,<dt>
用于<dl>
下;<thead>
,<tbody>
,<tfoot>
,<tr>
,<td>
用于<table>
下;
严格嵌套约束
inline-Level
元素,仅可以包含文本或其它inline-Level
元素;<a>
里不可以嵌套交互式元素<a>
、<button>
、<select>
等;<p>
里不可以嵌套块级元素<div>、<h1>~<h6>、<p>、<ul>/<ol>/<li>、<dl>/<dt>/<dd>、<form>
等。 布尔值属性 HTML5 规范中disabled、checked、selected
等属性不用设置值。
<input type="text" disabled>
<input type="checkbox" value="1" checked>
<select>
<option value="1" selected>1</option>
</select>
2. 基本文本
- 使用 HTML5 doctype,不区分大小写。
<!DOCTYPE html>
- 声明文档使用的字符编码
<meta charset="utf-8">
- 优先使用 IE 最新版本和 Chrome
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
例:
3.SEO 优化
- 页面描述
- 每个网页都应有一个不超过 150 个字符且能准确反映网页内容的描述标签。文档
<meta name="description" content="不超过150个字符"> <!-- 页面描述 -->
- 页面关键词
<meta name="keywords" content=""> <!-- 页面关键词 -->
- 定义页面标题
<title>标题</title>
- 定义网页作者
<meta name="author" content="name, email@gmail.com"> <!-- 网页作者 -->
- 定义网页搜索引擎索引方式,robotterms是一组使用英文逗号「,」分割的值,通常有如下几种取值:none,noindex,nofollow,all,index和follow。文档
<meta name="robots" content="index,follow"> <!-- 搜索引擎抓取 -->
4. 可选标签
为移动设备添加 viewport
<meta name ="viewport" content ="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no"> <!-- `width=device-width` 会导致 iPhone 5 添加到主屏后以 WebApp 全屏模式打开页面时出现黑边 http://bigc.at/ios-webapp-viewport-meta.orz -->
content 参数:
- width viewport 宽度(数值/device-width)
- height viewport 高度(数值/device-height)
- initial-scale 初始缩放比例
- maximum-scale 最大缩放比例
- minimum-scale 最小缩放比例
- user-scalable 是否允许用户缩放(yes/no)
页面窗口自动调整到设备宽度,并禁止用户缩放页面
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
电话号码识别
iOS Safari ( Android 或其他浏览器不会) 会自动识别看起来像电话号码的数字,将其处理为电话号码链接,比如:
- 7位数字,形如:1234567
- 带括号及加号的数字,形如:(+86)123456789
- 双连接线的数字,形如:00-00-00111
- 11位数字,形如:13800138000
邮箱地址的识别
在 Android ( iOS 不会)上,浏览器会自动识别看起来像邮箱地址的字符串,不论有你没有加上邮箱链接,当你在这个字符串上长按,会弹出发邮件的提示。
iOS 设备
- 添加到主屏后的标题
Android 设备
Android Lollipop 中增加 theme-color meta 标签,用来控制选项卡颜色。
<meta name="theme-color" content="#db5945">
禁止 Chrome 浏览器中自动提示翻译
<meta name="google" value="notranslate">
5.样式表、脚本加载
css 样式表在head标签内引用,js 脚本引用在 body 结束标签之前引用。
三.JavaScript规范
1.通用约定
注释原则
- As short as possible(如无必要,勿增注释):尽量提高代码本身的清晰性、可读性。
- As long as necessary(如有必要,尽量详尽):合理的注释、空行排版等,可以让代码更易阅读、更具美感。
命名
变量, 使用驼峰命名。
let loadingModules = {};
私有属性、变量和方法以下划线 _ 开头。
let _privateMethod = {};
常量, 使用全部字母大写。
let MAXCOUNT = 10;
枚举变量 使用 Pascal
命名法。
枚举的属性, 使用全部字母大写,单词间下划线分隔的命名方式。
let TargetState = {
READING: 1,
READED: 2,
APPLIED: 3,
READY: 4
};
boolean 类型的变量使用 is 或 has 开头。
let isReady = false;
let hasMoreCommands = false;
二元和三元操作符
操作符始终写在前一行, 以免分号的隐式插入产生预想不到的问题。
let x = a ? b : c;
let y = a ?
longButSimpleOperandB : longButSimpleOperandC;
let z = a ?
moreComplicatedB :
moreComplicatedC;
操作符也是如此:
let x = foo.bar().
doSomething().
doSomethingElse();
条件(三元)操作符 ( ? : )
三元操作符用于替代 if 条件判断语句。
// Not recommended
if (val != 0) {
return foo();
} else {
return bar();
}
// Recommended
return val ? foo() : bar();
&& 和 ||
二元布尔操作符是可短路的, 只有在必要时才会计算到最后一项。 例:
声明提升
- var 声明会被提升至该作用域的顶部,但它们赋值不会提升。let 和 const 被赋予了一种称为「暂时性死区(Temporal Dead Zones, TDZ)」的概念。这对于了解为什么 type of 不再安全相当重要。
- 匿名函数表达式的变量名会被提升,但函数内容并不会。
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function() {
console.log('anonymous function expression');
};
}
- 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会。
- 函数声明的名称和函数体都会被提升。
function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
具备强类型的设计
解释:
- 如果一个属性被设计为 boolean 类型,则不要使用 1 或 0 作为其值。对于标识性的属性,如对代码体积有严格要求,可以从一开始就设计为 number 类型且将 0 作为否定值。
- 从 DOM 中取出的值通常为 string 类型,如果有对象或函数的接收类型为 number 类型,提前作好转换,而不是期望对象、函数可以处理多类型的值。
2.函数设计
函数设计基本原则:低耦合,高内聚。(假如一个程序有50个函数;一旦你修改其中一个函数,其他49个函数都需要做修改,这就是高耦合的后果。)
函数长度
[建议] 一个函数的长度控制在 50 行以内。 将过多的逻辑单元混在一个大函数中,易导致难以维护。一个清晰易懂的函数应该完成单一的逻辑单元。复杂的操作应进一步抽取,通过函数的调用来体现流程。 特定算法等不可分割的逻辑允许例外。
例:
参数设计
[建议] 一个函数的参数控制在 6 个以内。 解释:
- 除去不定长参数以外,函数具备不同逻辑意义的参数建议控制在 6 个以内,过多参数会导致维护难度增大。
- 某些情况下,如使用 AMD Loader 的 require 加载多个模块时,其
callback
可能会存在较多参数,因此对函数参数的个数不做强制限制。
[建议] 通过 options 参数传递非数据输入型参数。 解释:
有些函数的参数并不是作为算法的输入,而是对算法的某些分支条件判断之用,此类参数建议通过一个 options 参数传递。
如下函数:
可以转换为下面的签名:
- boolean 型的配置项具备名称,从调用的代码上更易理解其表达的逻辑意义。
- 当配置项有增长时,无需无休止地增加参数个数,不会出现
removeElement(element, true, false, false, 3)
这样难以理解的调用代码。 - 当部分配置参数可选时,多个参数的形式非常难处理重载逻辑,而使用一个 options 对象只需判断属性是否存在,实现得以简化。 箭头函数
- 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。 为什么?因为箭头函数创造了新的一个 this 执行环境,通常情况下都能满足你的需求,而且这样的写法更为简洁。( 参考 Arrow functions - JavaScript | MDN )
为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个函数声明上。
// Not recommended
[1, 2, 3].map(function (x) {
const y = x + 1;
return x * y;
});
// Recommended
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
- 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 return 都省略掉。如果不是,那就不要省略。
为什么?语法糖。在链式调用中可读性很高。 为什么不?当你打算回传一个对象的时候。
// Recommended
[1, 2, 3].map(x => x * x);
// Recommended
[1, 2, 3].reduce((total, n) => {
return total + n;
}, 0);
函数和变量
同一个函数内部,局部变量的声明必须置于顶端 因为即使放到中间,js解析器也会提升至顶部(hosting)
// Recommended
var clear = function(el) {
var id = el.id,
name = el.getAttribute("data-name");
.........
return true;
}
// Not recommended
var clear = function(el) {
var id = el.id;
......
var name = el.getAttribute("data-name");
.........
return true;
}
块内函数必须用局部变量声明
// Not recommended
var call = function(name) {
if (name == "hotel") {
function foo() {
console.log("hotel foo");
}
}
foo && foo();
}
// Recommended
var call = function(name) {
var foo;
if (name == "hotel") {
foo = function() {
console.log("hotel foo");
}
}
foo && foo();
}
引起的bug:第一种写法foo的声明被提前了; 调用call时:第一种写法会调用foo函数,第二种写法不会调用foo函数
空函数
[建议] 对于性能有高要求的场合,建议存在一个空函数的常量,供多处使用共享。
示例:
3.面向对象
[强制] 类的继承方案,实现时需要修正 constructor。
解释:
通常使用其他 library 的类继承方案都会进行 constructor 修正。如果是自己实现的类继承方案,需要进行 constructor 修正。
示例:
示例
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私有变量的值
使用 extends 继承。
解释: extends 是一个内建的原型继承方法并且不会破坏 instanceof。
// Not recommended
const inherits = require('inherits');function PeekableQueue(contents) {
Queue.apply(this, contents);
}inherits(PeekableQueue, Queue);PeekableQueue.prototype.peek = function() {
return this._queue[0];
}
// Recommended
class PeekableQueue extends Queue {
peek() {
return this._queue[0];
}
}
[建议] 属性在构造函数中声明,方法在原型中声明。
解释: 原型对象的成员被所有实例共享,能节约内存占用。所以编码时我们应该遵守这样的原则:原型对象包含程序不会修改的成员,如方法函数或配置项。
function TextNode(value, engine) {
this.value = value;
this.engine = engine;
}
TextNode.prototype.clone = function () {
return this;
};
[强制] 自定义事件的 事件名 必须全小写。
解释: 在 JavaScript 广泛应用的浏览器环境,绝大多数 DOM 事件名称都是全小写的。为了遵循大多数 JavaScript 开发者的习惯,在设计自定义事件时,事件名也应该全小写。
[强制] 自定义事件只能有一个 event 参数。 如果事件需要传递较多信息,应仔细设计事件对象。
解释:
一个事件对象的好处有:
- 顺序无关,避免事件监听者需要记忆参数顺序。
- 每个事件信息都可以根据需要提供或者不提供,更自由。
- 扩展方便,未来添加事件信息时,无需考虑会破坏监听器参数形式而无法向后兼容。
[建议] 避免修改外部传入的对象。
解释:
JavaScript 因其脚本语言的动态特性,当一个对象未被 seal 或 freeze 时,可以任意添加、删除、修改属性值。 但是随意地对 非自身控制的对象 进行修改,很容易造成代码在不可预知的情况下出现问题。因此,设计良好的组件、函数应该避免对外部传入的对象的修改。 下面代码的 selectNode 方法修改了由外部传入的 datasource 对象。如果 datasource 用在其它场合(如另一个 Tree 实例)下,会造成状态的混乱。
4.性能优化
避免不必要的 DOM 操作
浏览器遍历 DOM 元素的代价是昂贵的。最简单优化 DOM 树查询的方案是,当一个元素出现多次时,将它保存在一个变量中,就避免多次查询 DOM 树了。
缓存数组长度
循环无疑是和 JavaScript 性能非常相关的一部分。通过存储数组的长度,可以有效避免每次循环重新计算。
注: 虽然现代浏览器引擎会自动优化这个过程,但是不要忘记还有旧的浏览器。
var arr = new Array(1000),
len, i;// Recommended - size is calculated only 1 time and then stored
for (i = 0, len = arr.length; i < len; i++) {
}
// Not recommended - size needs to be recalculated 1000 times
for (i = 0; i < arr.length; i++) {
}
优化 JavaScript 的特征
- 编写可维护的代码
- 单变量模式
- Hoisting:把所有变量声明统一放到函数的起始位置 (在后部声明的变量也会被JS视为在头部定义,由此会产生问题)
- 不要扩充内置原型(虽然给Object(), Function()之类的内置原型增加属性和方法很巧妙,但是会破坏可维护性)
- 不要用隐含的类型转换
- 不要用 eval()
- 用 parseInt() 进行数字转换
- (规范)左大括号的位置
- 构造器首字母大写
- 写注释
- 不要用 void
- 不要用 with 语句
- 不要用 continue 语句
- 尽量不要用位运算
优化 JavaScript 执行
在页面加载的时候,通常会有很多脚本等待执行,但你可以设定优先级。下面的顺序就是基于用户反馈设定的优先级:
- 改变页面视觉习性的脚本绝对要首先执行。这包括任何的字体调整、盒子布局、或IE6 pngfix。
- 页面内容初始化
- 页面标题、顶部导航和页脚的初始化
- 绑定事件处理器
- 网页流量分析、Doubleclick,以及其他第三方脚本
异步加载第三方内容
当你无法保证嵌入第三方内容比如 Youtube 视频或者一个 like/tweet 按钮可以正常工作的时候,你需要考虑用异步加载这些代码,避免阻塞整个页面加载。
5.调试
浏览器的怪异性会不可避免地产生一些问题。有几个工具可以帮助优化代码的健全性和加载速度。推荐先在Google Chrome和Firefox做开发,然后用Safari上和Opera,最后针对IE用条件判断做一些额外的微调。下面列出的是一些有帮助的调试器和速度分析器:
- Google Chrome: Developer Tools
- Firefox: Firebug, Page Speed, YSlow
- Safari: Web Inspector
- Opera: Dragonfly
- Internet Explorer 6-7: Developer Toolbar
- Internet Explorer 8-10: Developer Tools
四.CSS规范
1.通用约定
声明顺序
相关的属性声明应当归为一组,并按照下面的顺序排列:
- Positioning
- Box model
- Typographic
- Visual
- Misc
由于定位(positioning)可以从正常的文档流中移除元素,并且还能覆盖盒模型(box model)相关的样式,因此排在首位。盒模型排在第二位,因为它决定了组件的尺寸和位置。 其他属性只是影响组件的 内部 或者是不影响前两组属性,因此排在后面。
完整的属性列表及其排列顺序请参考 Bootstrap property order for Stylelint。
.declaration-order {
/* Positioning */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 100;
/* Box-model */
display: block;
float: right;
width: 100px;
height: 100px;
/* Typography */
font: normal 13px "Helvetica Neue", sans-serif;
line-height: 1.5;
color: #333;
text-align: center;
/* Visual */
background-color: #f5f5f5;
border: 1px solid #e5e5e5;
border-radius: 3px;
/* Misc */
opacity: 1;
}
链接的样式顺序
a:link -> a:visited -> a:hover -> a:active(LoVeHAte)
命名
组成元素
- 命名必须由单词、中划线①或数字组成;
- 不允许使用拼音(约定俗成的除外,如:youku, baidu),尤其是缩写的拼音、拼音与英文的混合。 不推荐:
.xiangqing { sRules; }
.news_list { sRules; }
.zhuti { sRules; }
推荐:
.detail { sRules; }
.news-list { sRules; }
.topic { sRules; }
①使用中划线 “-” 作为连接字符,而不是下划线 "_"。 2种方式都有不少支持者,但 "-" 能让你少按一次shift键,并且更符合CSS原生语法,所以只选一种目前普遍使用的方式
词汇规范
- 不依据表现形式来命名;
- 可根据内容来命名;
- 可根据功能来命名。 不推荐:
left, right, center, red, black
推荐:
nav, aside, news, type, search
缩写规范
- 保证缩写后还能较为清晰保持原单词所能表述的意思;
- 使用业界熟知的或者约定俗成的。
不推荐:
navigation => navi
header => head
description => des
推荐:
navigation => nav
header => hd
description => desc
前缀规范
前缀 | 说明 | 示例 |
---|---|---|
g- | 全局通用样式命名,前缀g全称为global,一旦修改将影响全站样式 | g-mod |
m- | 模块命名方式 | m-detail |
ui- | 组件命名方式 | ui-selector |
js- | 所有用于纯交互的命名,不涉及任何样式规则。JSer拥有全部定义权限 | js-switch |
选择器必须是以某个前缀开头
不推荐:
.info { sRules; }
.current { sRules; }
.news { sRules; }
这样将会带来不可预知的管理麻烦以及沉重的包袱。你永远也不会知道哪些样式名已经被用掉了,如果你是一个新人,你可能会遭遇,你每定义个样式名,都有同名的样式已存在,然后你只能是换样式名或者覆盖规则。
推荐:
.m-detail .info { sRules; }
.m-detail .current { sRules; }
.m-detail .news { sRules; }
所有的选择器必须是以 g-, m-, ui- 等有前缀的选择符开头的,意思就是说所有的规则都必须在某个相对的作用域下才生效,尽可能减少全局污染。 js- 这种级别的className完全交由JSer自定义,但是命名的规则也可以保持跟重构一致,比如说不能使用拼音之类的
选择器权重等级
a = 行内样式style。 b = ID选择器的数量。 c = 类、伪类和属性选择器的数量。 d = 类型选择器和伪元素选择器的数量。
选择器 | 等级 (a,b,c,d) |
---|---|
style=”” | 1,0,0,0 |
#wrapper #content {} | 0,2,0,0 |
#content .dateposted {} | 0,1,1,0 |
div#content {} | 0,1,0,1 |
#content p {} | 0,1,0,1 |
#content {} | 0,1,0,0 |
p.comment .dateposted {} | 0,0,2,1 |
div.comment p {} | 0,0,1,2 |
.comment p {} | 0,0,1,1 |
p.comment {} | 0,0,1,1 |
.comment {} | 0,0,1,0 |
div p {} | 0,0,0,2 |
p {} | 0,0,0,1 |
简写/省略/缩写
属性值书写尽量使用缩写
CSS很多属性都支持缩写shorthand (例如 font ) 尽量使用缩写,甚至只设置一个值。 使用缩写可以提高代码的效率和方便理解。
省略0后面的单位
非必要的情况下 0 后面不用加单位。
padding: 0;
margin: 0;
省略0开头小数点前面的0
值或长度在-1与1之间的小数,小数前的 0 可以忽略不写。
font-size: .8em;
省略URI外的引号
不要在 url() 里用 ( “” , ” ) 。
@import url(//www.google.com/css/go.css);
十六进制尽可能使用3个字符
加颜色值时候会用到它,使用3个字符的十六进制更短与简洁。
/* Not recommended */
color: #eebbcc;
/* Recommended*/
color: #ebc;
2.Less 和 Sass
- 操作符 为了提高可读性,在圆括号中的数学计算表达式的数值、变量和操作符之间均添加一个空格。
// Not recommended
.element {
margin: 10px 0 @variable*2 10px;
}
// Recommended
.element {
margin: 10px 0 (@variable * 2) 10px;
}
- 嵌套 避免不必要的嵌套。因为虽然可以使用嵌套,但是并不意味着应该使用嵌套。只有在必须将样式限制在父元素内(也就是后代选择器),并且存在多个需要嵌套的元素时才使用嵌套。
- 代码组织 代码按一下顺序组织:
- @import
- 变量声明
- 样式声明
@import "mixins/size.less";
@default-text-color: #333;
.page {
width: 960px;
margin: 0 auto;
}
混入(Mixin)
- 在定义 mixin 时,如果 mixin 名称不是一个需要使用的 className,必须加上括号,否则即使不被调用也会输出到 CSS 中。
- 如果混入的是本身不输出内容的 mixin,需要在 mixin 后添加括号(即使不传参数),以区分这是否是一个 className。
- 每个模块必须是一个独立的样式文件,文件名与模块名一致;
- 模块样式的选择器必须以模块名开头以作范围约定;
任何超过3级的选择器,需要思考是否必要,是否有无歧义的,能唯一命中的更简短的写法
3.hack规范
- 尽可能的减少对Hack的使用和依赖,如果在项目中对Hack的使用太多太复杂,对项目的维护将是一个巨大的挑战;
- 使用其它的解决方案代替Hack思路;
- 如果非Hack不可,选择稳定且常用并易于理解的。
.test {
color: #000; /* For all */
color: #111\9; /* For all IE */
color: #222\0; /* For IE8 and later, Opera without Webkit */
color: #333\9\0; /* For IE8 and later */
color: #444\0/; /* For IE8 and later */
color: #666; /* For IE7 and earlier */
color: #777; /* For IE6 and earlier */
}
注:严谨且长期的项目,针对IE可以使用条件注释作为预留Hack或者在当前使用
IE条件注释语法:
<!--[if <keywords>? IE <version>?]>
<link rel="stylesheet" href="*.css" />
<![endif]-->
语法说明:
<keywords>
if条件共包含6种选择方式:是否、大于、大于或等于、小于、小于或等于、非指定版本
是否:指定是否IE或IE某个版本。关键字:空
大于:选择大于指定版本的IE版本。关键字:gt(greater than)
大于或等于:选择大于或等于指定版本的IE版本。关键字:gte(greater than or equal)
小于:选择小于指定版本的IE版本。关键字:lt(less than)
小于或等于:选择小于或等于指定版本的IE版本。关键字:lte(less than or equal)
非指定版本:选择除指定版本外的所有IE版本。关键字:!
<version>
目前的常用IE版本为6.0及以上,推荐酌情忽略低版本,把精力花在为使用高级浏览器的用户提供更好的体验上,另从IE10开始已无此特性
4.动画与转换
使用 transition 时应指定 transition-property。
示例:
/* good */
.box {
transition: color 1s, border-color 1s;
}
/* bad */
.box {
transition: all 1s;
}
在浏览器能高效实现的属性上添加过渡和动画。 在可能的情况下应选择这样四种变换:
- transform: translate(npx, npx);
- transform: scale(n);
- transform: rotate(ndeg);
- opacity: 0..1; 典型的,可以使用 translate 来代替 left 作为动画属性。
示例:一般在 Chrome 中,3D或透视变换(perspective transform)CSS属性和对 opacity 进行 CSS 动画会创建新的图层,在硬件加速渲染通道的优化下,GPU 完成 3D 变形等操作后,将图层进行复合操作(Compesite Layers),从而避免触发浏览器大面积 重绘 和 重排。
/* good */
.box {
transition: transform 1s;
}.box:hover {
transform: translate(20px); /* move right for 20px */
}
/* bad */
.box {
left: 0;
transition: left 1s;
}.box:hover {
left: 20px; /* move right for 20px */
}
动画的基本概念:
- 帧:在动画过程中,每一幅静止画面即为一“帧”;
- 帧率:即每秒钟播放的静止画面的数量,单位是
fps(Frame per second)
; - 帧时长:即每一幅静止画面的停留时间,单位一般是ms(毫秒);
- 跳帧(掉帧/丢帧):在帧率固定的动画中,某一帧的时长远高于平均帧时长,导致其后续数帧被挤压而丢失的现象。
一般浏览器的渲染刷新频率是
60 fps
,所以在网页当中,帧率如果达到50-60 fps
的动画将会相当流畅,让人感到舒适。
- 如果使用基于 javaScript 的动画,尽量使用
requestAnimationFrame
. 避免使用setTimeout
,setInterval
. - 避免通过类似
jQuery animate()-style
改变每帧的样式,使用 CSS 声明动画会得到更好的浏览器优化。 - 使用
translate
取代absolute
定位就会得到更好的 fps,动画会更顺滑。
5.性能优化
慎重选择高消耗的样式
高消耗属性在绘制前需要浏览器进行大量计算:
- box-shadows
- border-radius
- transparency
- transforms
- CSS filters(性能杀手)
正确使用 Display 的属性
Display 属性会影响页面的渲染,请合理使用。
- display: inline后不应该再使用 width、height、margin、padding 以及 float;
- display: inline-block 后不应该再使用 float;
- display: block 后不应该再使用 vertical-align;
- display: table-* 后不应该再使用 margin 或者 float;
提升 CSS 选择器性能
CSS 选择器是从右到左进行规则匹配。只要当前选择符的左边还有其他选择符,样式系统就会继续向左移动,直到找到和规则匹配的选择符,或者因为不匹配而退出。最右边选择符称之为关键选择器。
CSS 选择器的执行效率从高到低做一个排序:
- 1.id选择器(#myid)
- 2.类选择器(.myclassname)
- 3.标签选择器(div,h1,p)
- 4.相邻选择器(h1+p)
- 5.子选择器(ul < li)
- 6.后代选择器(li a)
- 7.通配符选择器(*)
- 8.属性选择器(a[rel="external"])
- 9.伪类选择器(a:hover, li:nth-child)
避免使用通用选择器
/* Not recommended */
.content * {color: red;}
浏览器匹配文档中所有的元素后分别向上逐级匹配 class
为 content
的元素,直到文档的根节点。因此其匹配开销是非常大的,所以应避免使用关键选择器是通配选择器的情况。
避免使用标签或 class 选择器限制 id 选择器
/* Not recommended */
button#backButton {…}
/* Recommended */
#newMenuIcon {…}
避免使用标签限制 class 选择器
/* Not recommended */
treecell.indented {…}
/* Recommended */
.treecell-indented {…}
/* Much to recommended */
.hierarchy-deep {…}
避免使用多层标签选择器。
使用 class 选择器替换,减少css查找
/* Not recommended */
treeitem[mailfolder="true"] > treerow > treecell {…}
/* Recommended */
.treecell-mailfolder {…}
避免使用子选择器
/* Not recommended */
treehead treerow treecell {…}
/* Recommended */
treehead > treerow > treecell {…}
/* Much to recommended */
.treecell-header {…}
使用继承
/* Not recommended */
#bookmarkMenuItem > .menu-left { list-style-image: url(blah) }
/* Recommended */
#bookmarkMenuItem { list-style-image: url(blah) }
五:Vue规则说明
详细说明可查看 Vue官方风格指南
1.目录构建规范
── project-name
├── public 项目公共目录
│ ├── favicon.ico Favourite 图标
│ └── index.html 页面入口 HTML 模板
├── src 项目源码目录
│ ├── main.js 入口js文件
│ ├── App.vue 根组件
│ ├── api 把所有请求数据的接口,按照模块在单独的JS文件中
│ │ ├── home.js 首页接口
│ │ ├── detail.js 详情页接口,等等
│ │ ···
│ ├── assets 静态资源目录,公共的静态资源,图片,字体等
│ │ ├── fonts 字体资源
│ │ ├── images 图片资源
│ │ ···
│ ├── common 公共脚本,样式,配置,等动态信息
│ │ ├── js 公共脚本
│ │ │ │── utlis.js 公共 JS 工具函数
│ │ │ │── config.js 公共配置
│ │ │ ···
│ │ ├── style 公共样式,可以是各种预处理语言
│ │ │ │── index.css 样式主文件
│ │ │ │── reset.css 重置样式
│ │ │ ···
│ │ ···
│ ├── components 公共组件目录
│ │ ├── confirm 弹框组件
│ │ │ └── index.vue
│ │ ├── scroll 局部内容滚动组件
│ │ │ └── index.vue
│ │ ···
│ ├── http 封装的 HTTP 请求方法
│ │ ├── axios.js Axios 的封装
│ │ ├── jsonp.js JSONP 的封装
│ │ ···
│ ├── store 数据中心
│ │ ├── state.js state 数据源,数据定义
│ │ ├── mutations.js 同步修改 state 数据的方法定义
│ │ ├── mutation-types.js 定义 Mutations 的类型
│ │ ├── actions.js 异步修改 state 数据的方法定义
│ │ ├── getters.js 获取数据的方法定义
│ │ └── index.js 数据中心入口文件
│ ├── routes 前端路由
│ │ └── index.js
│ └── views 页面目录,所有业务逻辑的页面都应该放这里
│ ├── home 应用首页
│ │ └── index.vue
│ ···
├── .env.development Vue 开发环境的配置
├── .env.production Vue 生成环境的配置
├── .eslintrc.js Eslint 配置文件
├── .gitignore Git 忽略文件
├── package.json 包依赖文件
├── package-lock.json 依赖包版本管理文件
├── README.md 项目介绍
├── vue.config.js vue/cli 项目脚手架配置
···
2.src 文件说明
- src/api 模块的请求方法。应该参考 src/views 中的直接子文件夹的结构,做映射。
- src/assets 项目的静态资源文件。虽然是静态文件(图片,字体,等),但是还是经过 webpack 处理会好一些,因为有些比较小的文件会被打包到文件,减少请求和压缩第三方包。这个模块的文件,如果还需要扩展,一个单词作为文件夹名字!保持简洁性。
- src/common 公共的动态的脚本、样式。在实际中,样式可能是各种预处理语言写的,请自行组织目录结构。
- src/components 公共组件。放置项目中抽象的基础和业务组件。你应该为每个组件都单独建一个文件夹,以做好组件之间的隔离,并且有一个默认的文件:index.vue 文件,以便引入组件时少写几个字母。默认组件中的文件是一个最小的单位,不应该有依赖其他组件,或操作 state 状态等行为。
- src/http 主要是关于请求方法的封装。建议开发过程中不要修改,因为会影响到全局。
- src/store 数据中心。这部分内容比较多,独立出来,详情参考:数据中心
- src/router 页面路由。默认所有路由映射写在一个文件,如果需要路由模块化,请自行组织。
- src/views 所有业务逻辑的页面。
3.组件/实例的选项顺序
- name
- components / directives / filters
- extends / mixins
- model / props / propsData
- data / computed
- watch / 生命周期钩子
- methods
<script>
export default {
name: 'ExampleName', // 这个名字推荐:大写字母开头驼峰法命名。
props: {}, // Props 定义。
components: {}, // 组件定义。
directives: {}, // 指令定义。
mixins: [], // 混入 Mixin 定义。
data () { // Data 定义。
return {
dataProps: '' // Data 属性的每一个变量都需要在后面写注释说明用途
}
},
computed: {}, // 计算属性定义。
created () {}, // 生命钩子函数,按照他们调用的顺序。
mounted () {}, // 挂载到元素。
activated () {}, // 使用 keep-alive 包裹的组件激活触发的钩子函数。
deactivated () {}, // 使用 keep-alive 包裹的组件离开时触发的钩子函数。
watch: {}, // 属性变化监听器。
methods: { // 组件方法定义。
publicbFunction () {} // 公共方法的定义,可以提供外面使用
_privateFunction () {} // 私有方法,下划线定义,仅供组件内使用。
}
}
</script>
4.元素特性的顺序
- is
- v-for / v-if / v-else-if / v-else / v-show / v-cloak
- v-pre / v-once
- id
- ref / key / slot
- v-model
- v-on
- v-html / v-text
5.事件的控制
- 不要使用 this.parent / this.root 进行控制, 会导致流程难以跟踪. 可使用事件中心进行代替.
- 事件中心每添加事件, 需要注明事件的 名称 / 来源文件 / 流向文件, 以及事件的作用.
- 事件移除时需要移除事件中心对应事件注明.
- 组件当中有原生绑定事件, 注意在组建注销前进行移除
六.React规则说明
详细规则参考 React官方文档
"react/display-name": [1, { // 指定组件的 `displayName`
ignoreTranspilerName: false, // `true` 忽略编译器设置的组件称,
}],
"react/forbid-prop-types": [1, { // 给组件传入不相关的 `props`
forbid: ["any"],
}],
"react/jsx-boolean-value": 2, // `true` 值简写 <A show />,
"react/jsx-closing-bracket-location": [2, 'line-aligned'], // 组件多个 `props` 格式缩进,
"react/jsx-clos,ing-tag-location": 2, // 关闭标签位置与开始标签平齐或者同行,
"react/jsx-curly-newline": {
multiline: 'consistent', // 多行强制两个花括号内都直接有一个换行符,
singleline: 'consistent', // 单行没有换行符
},
"react/jsx-curly-spacing": 2, // 对象`props` 空格缩进 => name={ test},
"react/jsx-equals-spacing": [2, "always"], // `props` 等号之间没有空格
"react/jsx-indent-props": [2, 'tab'], // 组件前两个空格缩进
"react/jsx-key": 2, // 渲染列表必须带 `key`
"react/jsx-max-props-per-line": [2, { // 单行行最多的 `props` 数量
when: 'multiline'
}],
"react/jsx-no-bind": [1, { // 不用 `bind` 绑定事件
ignoreDOMComponents: true, // 原生 `DOM` 不做限制
allowArrowFunctions: true, // 允许少量的箭头函数
}],
"react/jsx-no-duplicate-props": 2, // 不允许 `props` 属性重复定义 => <Hello name="John" name="John" />
"react/jsx-no-undef": 2, // 未定义就使用的组件变量
"react/jsx-props-no-multi-spaces": 2, // 多个 `props` 之间只有一个格
"react/jsx-one-expression-per-line": 2, // 单行只有一个表达式
"react/jsx-pascal-case": 2, // 组件名大驼峰规范 => 错误范例: <TEST_COMPONENT />
"react/jsx-sort-props": [2, {
callbacksLast: true, // `callback` 放最后面
shorthandFirst: true, // 简写 `props` 放最前面
}],
"react/jsx-tag-spacing": 2, // 封闭括号相关的空格
"react/jsx-uses-react": 1, // 引入 `react` 包之后却没有使用它(声明组件)
"react/jsx-uses-vars": 1, // 组件变量声明之后但是未使用
"react/jsx-wrap-multilines": 2, // `DOM` 块最好用括号以表达清晰
"react/no-danger": 2, // 不允许 `dangerouslySetInnerHTML` 这个 `props`
"react/no-multi-comp": 1, // 每个 `jsx` 文件只能声明一个组件
"react/no-unknown-property": 2, // 未知的 `DOM` 属性
"react/no-array-index-key": 2, // 数组的 `key` 不能用 `index`
"react/no-children-prop": 2, // 不能将 `children` 作为 `props`
结语
坚持一致性的原则。 一个团队的代码风格如果统一了,首先可以培养良好的协同和编码习惯,其次可以减少无谓的思考,再次可以提升代码质量和可维护性。
参考文档:
关注公众号,一起学习: