一般规范
文件/资源命名
在 web 项目中,所有的文件名应该都遵循同一命名约定。以可读性而言,减号(-)是用来分隔文件名的不二之选。同时它也是常见的 URL 分隔符(i.e. //example.com/blog/my-blog-entry or //s.example.com/images/big-black-background.jpg), 所以理所当然的,减号应该也是用来分隔资源名称的好选择。
请确保文件命名总是以字母开头而不是数字。而以特殊字符开头命名的文件,一般都有特殊的含义与用处(比如 compass 中的下划线就是用来标记跳过直接编译的文件用的)。
资源的字母名称必须全为小写,这是因为在某些对大小写字母敏感的操作系统中,当文件通过工具压缩混淆后,或者人为修改过后,大小写不同而导致引用文件不同的错误,很难被发现。
还有一些情况下,需要对文件增加前后缀或特定的扩展名(比如 .min.js, .min.css),抑或一串前缀(比如 3fa89b.main.min.css)。这种情况下,建议使用点分隔符来区分这些在文件名中带有清晰意义的元数据。
协议
不要指定引入资源所带的具体协议。
当引入图片或其他媒体文件,还有样式和脚本时,URLs 所指向的具体路径,不要指定协议部分(http:, https:),除非这两者协议都不可用。
不指定协议使得 URL 从绝对的获取路径转变为相对的,在请求资源协议无法确定时非常好用,而且还能为文件大小节省几个字节。
文本缩进
一次缩进两个空格。
HTML规范
标签闭合
HTML 中最好不要将无内容元素的标签闭合,例如:使用 <br> 而非 <br />.
空白元素指的是以下元素:area, base, br, col, command, embed, hr, img, input, keygen, link, meta, param, source, track, wbr.
脚本加载
出于性能考虑,脚本异步加载很关键。一段脚本放置在 <head> 内,比如 <script src=”main.js”></script>,其加载会一直阻塞 DOM 解析,直至它完全地加载和执行完毕。这会造成页面显示的延迟。特别是一些重量级的脚本,对用户体验来说那真是一个巨大的影响。
异步加载脚本可缓解这种性能影响。如果只需兼容 IE10+,可将 HTML5 的 async 属性加至脚本中,它可防止阻塞 DOM 的解析,甚至你可以将脚本引用写在 <head> 里也没有影响。
如需兼容老旧的浏览器,实践表明可使用用来动态注入脚本的脚本加载器。你可以考虑 yepnope 或 labjs。注入脚本的一个问题是:一直要等到 CSS 对象文档已就绪,它们才开始加载(短暂地在 CSS 加载完毕之后),这就对需要及时触发的 JS 造成了一定的延迟,这多多少少也影响了用户体验吧。
终上所述,兼容老旧浏览器(IE9-)时,应该遵循以下最佳实践。
脚本引用写在 body 结束标签之前,并带上 async 属性。这虽然在老旧浏览器中不会异步加载脚本,但它只阻塞了 body 结束标签之前的 DOM 解析,这就大大降低了其阻塞影响。而在现代浏览器中,脚本将在 DOM 解析器发现 body 尾部的 script 标签才进行加载,此时加载属于异步加载,不会阻塞 CSSOM(但其执行仍发生在 CSSOM 之后)。
语义化
根据元素(有时被错误地称作“标签”)其被创造出来时的初始意义来使用它。打个比方,用 heading 元素来定义头部标题,p 元素来定义文字段落,用 a 元素来定义链接锚点,等等。
有根据有目的地使用 HTML 元素,对于可访问性、代码重用、代码效率来说意义重大。
多媒体回溯
对页面上的媒体而言,像图片、视频、canvas 动画等,要确保其有可替代的接入接口。图片文件我们可采用有意义的备选文本(alt),视频和音频文件我们可以为其加上说明文字或字幕。
提供可替代内容对可用性来说十分重要。试想,一位盲人用户如何能知晓一张图片是什么,要是没有 @alt 的话。
(图片的 alt 属性是可不填写内容的,纯装饰性的图片就可用这么做:alt=”“)。
关注点分离
理解 web 中如何和为何区分不同的关注点,这很重要。这里的关注点主要指的是:信息(HTML 结构)、外观(CSS)和行为(JavaScript)。为了使它们成为可维护的干净整洁的代码,我们要尽可能的将它们分离开来。
严格地保证结构、表现、行为三者分离,并尽量使三者之间没有太多的交互和联系。
就是说,尽量在文档和模板中只包含结构性的 HTML;而将所有表现代码,移入样式表中;将所有动作行为,移入脚本之中。
在此之外,为使得它们之间的联系尽可能的小,在文档和模板中也尽量少地引入样式和脚本文件。
清晰的分层意味着:
- 不使用超过一到两张样式表(i.e. main.css, vendor.css)
- 不使用超过一到两个脚本(学会用合并脚本)
- 不使用行内样式(<style>.no-good {}</style>)
- 不在元素上使用 style 属性(<hr style=”border-top: 5px solid black”>)
- 不使用行内脚本(<script>alert(‘no good’)</script>)
- 不使用表象元素(i.e. <b>, <u>, <center>, <font>, <b>)
- 不使用表象 class 名(i.e. red, left, center)
HTML 内容至上
HTML 标签的目的,就是为了不断地展示内容信息。
不要引入一些特定的 HTML 结构来解决一些视觉设计问题
不要将 img 元素当做专门用来做视觉设计的元素
Type 属性
省略样式表与脚本上的 type 属性。鉴于 HTML5 中以上两者默认的 type 值就是 text/css 和 text/javascript,所以 type 属性一般是可以忽略掉的。甚至在老旧版本的浏览器中这么做也是安全可靠的。
格式化规则
在每一个块状元素,列表元素和表格元素后,加上一新空白行,并对其子孙元素进行缩进。内联元素写在一行内,块状元素还有列表和表格要另起一行。
(如果由于换行的空格引发了不可预计的问题,那将所有元素并入一行也是可以接受的,格式警告总好过错误警告)。
HTML 引号
使用双引号(” “) 而不是单引号(’ ‘) 。
CSS 和 Sass (SCSS) 规范
ID and class 命名
ID和class(类)名总是使用可以反应元素目的和用途的名称,或其他通用名称。
合理的避免使用ID
一般情况下ID不应该被应用于样式。
ID的样式不能被复用并且每个页面中你只能使用一次ID。使用ID唯一有效的是确定网页或整个站点中的位置。尽管如此,你应该始终考虑使用class,而不是id,除非只使用一次。
另一个反对使用ID的观点是含有ID选择器权重很高。一个只包含一个ID选择器权重高于包含1000个class(类)名的选择器,这使得它很奇怪。
CSS选择器中避免标签名
当构建选择器时应该使用清晰, 准确和有语义的class(类)名。不要使用标签选择器。 如果你只关心你的class(类)名,而不是你的代码元素,这样会更容易维护。
从分离的角度考虑,在表现层中不应该分配html标记/语义。它可能是一个有序列表需要被改成一个无序列表,或者一个div将被转换成article。
如果你只使用具有实际意义的class(类)名,并且不使用元素选择器,那么你只需要改变你的html标记,而不用改动你的CSS。
尽可能的精确
很多前端开发人员写选择器链的时候不使用直接子选择器。有时,这可能会导致疼痛的设计问题并且有时候可能会很耗性能。然而,在任何情况下,这是一个非常不好的做法。如果你不写很通用的,需要匹配到DOM末端的选择器, 你应该总是考虑直接子选择器。
缩写属性
CSS提供了各种缩写属性(如 font 字体)应该尽可能使用,即使在只设置一个值的情况下。使用缩写属性对于代码效率和可读性是有很有用的。
0 和 单位
省略“0”值后面的单位。不要在0值后面使用单位,除非有值。
十六进制表示法
在可能的情况下,使用3个字符的十六进制表示法。颜色值允许这样表示,3个字符的十六进制表示法更简短。始终使用小写的十六进制数字。
ID 和 Class(类) 名的分隔符
使用连字符(中划线)分隔ID和Class(类)名中的单词。为了增强可理解性,在选择器中不要使用除了连字符(中划线)以为的任何字符(包括没有)来连接单词和缩写。
另外,作为该标准,预设属性选择器能识别连字符(中划线)作为单词[attribute|=value]的分隔符,所以最好的坚持使用连字符作为分隔符。
声明顺序
这是一个选择器内书写CSS属性顺序的大致轮廓。这是为了保证更好的可读性和可扫描重要。作为最佳实践,我们应该遵循以下顺序(应该按照下表的顺序):
结构性属性:
display
position, left, top, right etc.
overflow, float, clear etc.
margin, padding
表现性属性:
background, border etc.
font, text
声明结束
为了保证一致性和可扩展性,每个声明应该用分号结束,每个声明换行。
属性名结束
属性名的冒号后使用一个空格。出于一致性的原因,属性和值(但属性和冒号之间没有空格)的之间始终使用一个空格。
选择器和声明分离
每个选择器和属性声明总是使用新的一行。
规则分隔
规则之间始终有一个空行(双换行符)分隔。
CSS引号
属性选择器或属性值用双引号(” “),而不是单引号(’ ‘)括起来。
URI值(url())不要使用引号。
选择器嵌套 (SCSS)
在Sass中你可以嵌套选择器,这可以使代码变得更清洁和可读。嵌套所有的选择器,但尽量避免嵌套没有任何内容的选择器。
如果你需要指定一些子元素的样式属性,而父元素没有什么样式属性,可以使用常规的CSS选择器链。这将防止您的脚本看起来过于复杂。
嵌套顺序和父级选择器(SCSS)
当使用Sass的嵌套功能的时候,重要的是有一个明确的嵌套顺序,以下内容是一个SCSS块应具有的顺序。
- 当前选择器的样式属性
- 父级选择器的伪类选择器 (:first-letter, :hover, :active etc)
- 伪类元素 (:before and :after)
- 父级选择器的声明样式 (.selected, .active, .enlarged etc.)
- 用Sass的上下文媒体查询
- 子选择器作为最后的部分
JavaScript 规范
IIFE(立即执行的函数表达式)
无论何时,想要创建一个新的封闭的定义域,那就用 IIFE。它不仅避免了干扰,也使得内存在执行完后立即释放。
所有脚本文件建议都从 IIFE 开始。
立即执行的函数表达式的执行括号应该写在外包括号内。虽然写在内还是写在外都是有效的,但写在内使得整个表达式看起来更像一个整体,因此推荐这么做。
全局命名空间污染与 IIFE
总是将代码包裹成一个 IIFE(Immediately-Invoked Function Expression),用以创建独立隔绝的定义域。这一举措可防止全局命名空间被污染。
IIFE 还可确保你的代码不会轻易被其它全局命名空间里的代码所修改(i.e. 第三方库,window 引用,被覆盖的未定义的关键字等等)。
如果你想引用全局变量或者是外层 IIFE 的变量,可以通过下列方式传参:
(function($, w, d){
'use strict';
$(function() {
w.alert(d.querySelectorAll('div').length);
});
}(jQuery, window, document));
严格模式
ECMAScript 5 严格模式可在整个脚本或独个方法内被激活。它对应不同的 javascript 语境会做更加严格的错误检查。严格模式也确保了 javascript 代码更加的健壮,运行的也更加快速。
严格模式会阻止使用在未来很可能被引入的预留关键字。
你应该在你的脚本中启用严格模式,最好是在独立的 IIFE 中应用它。避免在你的脚本第一行使用它而导致你的所有脚本都启动了严格模式,这有可能会引发一些第三方类库的问题。
采用严格模式带来的好处是,当你手误输入错误的变量名时,它可以通过报错信息来帮助你定位错误出处。
理解 JavaScript 的定义域和定义域提升
所有的变量以及方法,应当定义在 function 内的首行。
只用一个 var 关键字声明,多个变量用逗号隔开。
把赋值尽量写在变量申明中。
总是使用带类型判断的比较判断
总是使用 === 精确的比较操作符,避免在判断的过程中,由 JavaScript 的强制类型转换所造成的困扰。
如果你使用 === 操作符,那比较的双方必须是同一类型为前提的条件下才会有效。
明智地使用真假判断
当我们在一个 if 条件语句中使用变量或表达式时,会做真假判断。if(a == true) 是不同于 if(a) 的。后者的判断比较特殊,我们称其为真假判断。这种判断会通过特殊的操作将其转换为 true 或 false,下列表达式统统返回 false:false, 0, undefined, null, NaN, ”(空字符串).
这种真假判断在我们只求结果而不关心过程的情况下,非常的有帮助。
变量赋值时的逻辑操作
逻辑操作符 || 和 && 也可被用来返回布尔值。如果操作对象为非布尔对象,那每个表达式将会被自左向右地做真假判断。基于此操作,最终总有一个表达式被返回回来。这在变量赋值时,是可以用来简化你的代码的。
分号
总是使用分号,因为隐式的代码嵌套会引发难以察觉的问题。
JavaScript 中语句要以分号结束,否则它将会继续执行下去,不管换不换行。
分号需要用在表达式的结尾,而并非函数声明的结尾。
语句块内的函数声明
切勿在语句块内声明函数,在 ECMAScript 5 的严格模式下,这是不合法的。函数声明应该在定义域的顶层。但在语句块内可将函数申明转化为函数表达式赋值给变量。
异常
基本上你无法避免出现异常,特别是在做大型开发时(使用应用开发框架等等)。
在没有自定义异常的情况下,从有返回值的函数中返回错误信息一定非常的棘手,更别提多不优雅了。不好的解决方案包括了传第一个引用类型来接纳错误信息,或总是返回一个对象列表,其中包含着可能的错误对象。以上方式基本上是比较简陋的异常处理方式。适时可做自定义异常处理。
在复杂的环境中,你可以考虑抛出对象而不仅仅是字符串(默认的抛出值)。
if(name === undefined) {
throw {
name: 'System Error',
message: 'A name should always be specified!'
}
}
切勿在循环中创建函数
在简单的循环语句中加入函数是非常容易形成闭包而带来隐患的。
eval 函数(魔鬼)
eval() 不但混淆语境还很危险,总会有比这更好、更清晰、更安全的另一种方案来写你的代码,因此尽量不要使用 eval 函数。
this 关键字
只在对象构造器、方法和在设定的闭包中使用 this 关键字。this 的语义在此有些误导。它时而指向全局对象(大多数时),时而指向调用者的定义域(在 eval 中),时而指向 DOM 树中的某一节点(当用事件处理绑定到 HTML 属性上时),时而指向一个新创建的对象(在构造器中),还时而指向其它的一些对象(如果函数被 call() 和 apply() 执行和调用时)。
正因为它是如此容易地被搞错,请限制它的使用场景:在构造函数中、在对象的方法中(包括由此创建出的闭包内)
首选函数式风格
函数式编程让你可以简化代码并缩减维护成本,因为它容易复用,又适当地解耦和更少的依赖。
例外:往往在重代码性能轻代码维护的情况之下,要选择最优性能的解决方案而非维护性高的方案(比如用简单的循环语句代替 forEach)。
不要使用 switch
switch 在所有的编程语言中都是个非常错误的难以控制的语句,建议用 if else 来替换它。
数组和对象字面量
用数组和对象字面量来代替数组和对象构造器。数组构造器很容易让人在它的参数上犯错。
修改内建对象的原型链
修改内建的诸如 Object.prototype 和 Array.prototype 是被严厉禁止的。修改其它的内建对象比如 Function.prototype,虽危害没那么大,但始终还是会导致在开发过程中难以 debug 的问题,应当也要避免。
自定义 toString() 方法
你可以通过自定义 toString() 来控制对象字符串化。这很好,但你必须保证你的方法总是成功并不会有其它副作用。如果你的方法达不到这样的标准,那将会引发严重的问题。如果 toString() 调用了一个方法,这个方法做了一个断言 ,当断言失败,它可能会输出它所在对象的名称,当然对象也需要调用 toString()。
字符串
统一使用单引号(’ ‘),不使用双引号(” “)。
以上内容来自:前端代码规范