前端代码标准和最佳实践
isobar的这个前端代码标准和最佳实践文档,涵盖了Web应用开发的方方面面,我们翻译了大部分章节,并做了注解。请仔细阅读用标记的段落。
关键词列表:
渐进增强;Combo Handler;Quirks Mode;浏览器盒子模型;选择器特殊性;Spacer Image;CSS Sprites;PNG8;
编写此文档的目的主要有两方面,第一,如何保持代码的一致性;第二,什么才是最佳实践。我们通过编码风格和约定保持一致,可以减轻代码维护的工作量,并降低风险,在将来代码出现问题的时候,方便我们排查错误。我们要保持最佳的编码习惯和做法,确保优化页面的加载和性能,并能更好地维护代码。
普遍性准则
前端开发的核心
1、 结构、表现、行为应该分离。
注:渐进增强(Progressive Enhancement)
名词定义:渐进增强是从一个功能可用的基本版本出发,然后逐步增加用户体验的丰富程度,并在应用增强功能之前先测试对该功能的支持性;最基本的可用性出发,在保证站点页面在低级浏览器中的可用性和可访问性的基础上,逐步增加功能及提高用户体验。辅助阅读:http://www.ryanbay.com/?p=97
一般性实践
缩进
对于所有代码语言,我们需要通过TAB缩进进行代码排版(使用空格字符),在文本编辑器击中TAB应相当于四个空格。
可读性 vs 压缩
我们在代码可读性和文件大小的取舍中,更倾向于良好的可读性。
在合适的地方,我们鼓励使用大量的缩进、换行来维持文件代码的可读性。开发者不需要刻意地压缩文件的体积大小,也不需要混淆使用JavaScript。
我们将使用服务器端或发布过程中自动minify和gzip的所有静态客户端文件,如CSS和JavaScript。
注:一般采用服务端的 Combo Handler 来对服务器端上传的未压缩、未混淆的JS和CSS文件做统一合并、压缩和混淆。
标签
标记定义了结构和轮廓的文档,并提供了一个结构化内容。
标记并不打算定义的外观和视觉表现,只用于感知网页的基本概念,如标题、段落、列表。HTML标记中表现样式的属性都应被弃用,样式的表现应该包含在样式表里。
HTML5
我们将在适当的时候使用HTML5 Doctype和HTML5特性。
我们将用W3C验证器测试我们的标记,以确保格式良好。100%有效的代码并不是我们的目标,但W3C验证确实有助于编写更易于维护的网站以及调试代码。Isobar不保证代码是100%有效,而是确保跨浏览器有统一的浏览体验。
模板
对于HTML5文档,我们将定制 H5BP。
文档类型
应该始终使用一个合适的 Doctype 来触发浏览器的标准模式。
我们应该始终避免Quirks Mode(怪异模式或兼容模式)。
HTML5的一个好的方面是,它简化了所需的代码量。无意义的属性已经有所下降,而且已大大简化了 Doctype 声明。因此建议文档类型应声明为HTML5的形式。
HTML5 Doctype
1.
<!DOCTYPE html>
注:怪异模式(quirks mode)
名词定义:指在计算机领域中,一些网页浏览器为了维持对较旧的网页设计的向后兼容性,而使用的一种技术,有别于严格遵循万维网联盟(W3C)与互联网工程任务组(IETF)标准而设计的「标准模式」。
为了与可能数量众多的网页维持兼容,现代的网页浏览器一般具有多种渲染模式:在「标准模式」(standards mode) 页面按照 HTML 与 CSS 的定义渲染,而在「quirks 模式」中则尝试模拟更旧的浏览器的行为。
在 quirks 模式和标准模式之间一个突出的不同是对 CSS IE盒模型缺陷的处理。
参考资料:What happens in Quirks Mode?
DOCTYPE声明 |
说明 |
IE8 |
FF |
Chrome |
<!DOCTYPE HTML> |
HTML5推荐的方式 |
CSS1Compat |
CSS1Compat |
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
严格模式声明并给出DTD URL |
CSS1Compat |
CSS1Compat |
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> |
严格模式声明但不给出DTD URL |
CSS1Compat |
CSS1Compat |
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
过渡模式并给出DTD URL |
CSS1Compat |
CSS1Compat |
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
过渡模式但不给出DTD URL |
BackCompat |
BackCompat |
字符编码
所有标记都应设置为utf – 8。它应该同时指定在 HTTP 报头和文档头部。
1.
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=UTF-8"
/>
In HTML5, just do:
1.
<
meta
charset
=
"utf-8"
>
通用标记指南
以下是一般指南,用于构建你的HTML标记。
- 使用实际的P元素分隔符而不是多段BR标签。
- 在适当的时候,利用DL(定义列表)和 BLOCKQUOTE。
- 使用UL,OL,DL 来表示一个项目列表,不要使用多个DIV嵌套模拟一个项目列表。
- 使用label 字段来标记表单中每一个字段,它的for属性把这个label与input field关联起来,使得用户可以点击这个label。设置cursor:pointer给label也是明智之举 !!
- 不要使用size属性来控制你输入框的大小,应该使用CSS来定义。
- 在一些闭合div后面加上html注释,以标示你关闭的是什么元素。有大量嵌套和缩进时这会很有帮助。
- 不要使用 tables来完成你的页面布局。
- 在适当的时候合理的运用table中的thead 、tbody、tfoot以及th等标签。
正确的表格:
引号属性
虽然HTML5规范定义了在那些可以自动识别空格的属性周围添加引号作为可选属性,但是所有的属性都应该添加引号。
1.
<
p
class
=
"line note"
data-attribute
=
"106"
>This is my paragraph of special text.</
p
>
CSS
Web页面的第二个组件是包含在样式表(Cascading Style Sheet,CSS)里的展示信息。
一般性编码原则
- 通过外部文件来添加CSS。CSS文件里尽可能不要用 # 。CSS总是应该在文档头部<head></head>之间。
- 使用 link 标记来引用外部CSS文件,永远不要使用 @import导入CSS。
- 不要使用内联样式(inline styling),因为维护的时候很难跟踪样式的规则。
- 确保同一个CSS文件文档只引用一次。
- 请用一个字体标准化文件,如YUI fonts.css,来定义各个标记的字体在不同浏览器中有统一表现!
- 文档中仅出现一次的元素,应使用ID,否则,使用class。
- 理解层叠和选择器特性,这样你就可以编写非常简洁、有效的代码。
- 编写CSS代码时,使用高效的选择器。如果可能,要避免使用运行效率低下的选择器。比如,我们应该避免 * 通配符选择器,避免不适当的ID选择器(如
div#myid
)或类选择器(如table.results
.)。这些之所以特别重要,是因为在Web应用程序中速度是至关重要的,可能有数以千计甚至数以万计的DOM元素需要被渲染。更多细节请参考: 在MDC上编写高效的CSS。
浏览器盒子模型
深入了解CSS和浏览器盒子模型(browser-based box model)是做好CSS布局非常必要的一个条件。
CSS校验
我们不使用 w3c的css校验器。
CSS格式化
我们应该保证,至少每一个选择器应该在单独的一行。声明应该是缩进的。
Classes vs. IDs
如果一些元素是文档中独一无二的,那么你应该只给他们一个ID属性。CLASS则是应用在具备同样属性样式的多个元素上。
选择器的命名规范
无论是ID选择器还是类选择器,我们应该遵循“它是什么(what it is)”而不是“它是什么样子的(what it looks like)”的原则进行命名。例如:一个class命名为 bigBlueText,如果它后来被改为了红色的小字体,那么该class命名就失去了意义,让人不好理解。
如果我们命名为 noteText,无论如何改变外观,它仍然是一个文本框,仍然有意义。
选择器(Selectors)
CSS Selectors Level 3规格说明书里介绍了一整套CSS选择器,非常有用。
Pseudo-classes
伪类(Pseudo-classes)能动态化样式内容。一些伪类从CSS1就存在了,如:visited, :hover
,有些从CSS2就存在了,如:first-child, :lang
。CSS3引入了16个伪类,都非常有用,请深入学习一下它们的用法。
特殊性(Specificity)
浏览器会计算一个选择器的权重,来决定应用哪一个CSS规则。如果两个选择器都要应用到一个元素上,那么特殊性更高的选择器胜出。
ID的特殊性比属性选择器的更高;属性选择器则比类选择器高。所以你可以尝试用id来增加特殊性。
计算特殊性
当你遇到一个又大又复杂的样式表时,你最好知道怎么计算特殊性,这样能让你的选择器更有效率。
特殊性的值可以看作是一个由四个数组成的一个组合,用 a,b,c,d 来表示它的四个位置。 依次比较 a,b,c,d 这个四个数比较其特殊性的大小。比如,a 值相同,那么 b 值大的组合特殊性会较大,以此类推。注意,W3C 中并不是把它作为一个 4 位数来看待的。
a,b,c,d 值的确定规则:
- 如果 HTML 标签的 'style' 属性中该样式存在,则记 a 为 1;
- 数一下选择器中 ID 选择器的个数作为 b 的值。比如,以上样式中包含 '#c1' 和 '#c2' 的选择器;
- 其他属性以及伪类(pseudo-classes)的总数量是 c 的值。比如,上面例子中的 '.con',':hover' 等;
- 元素名和伪元素的数量是 d 的值;比如上面例子中的 ‘div’。
(注:原文为:
- Element, Pseudo Element: d = 1 – (0,0,0,1)
- Class, Pseudo class, Attribute: c = 1 – (0,0,1,0)
- Id: b = 1 – (0,1,0,0)
- Inline Style: a = 1 – (1,0,0,0)
此处没有直接翻译。)
下面举一个实际例子:
使用!important将覆盖所有特殊性,无论它们值有多高。因此尽量不要用 !important。大多数情况下它都是不必要的。
Pixels vs. Ems
我们使用px计量单位来定义字体大小,因为它对文本提供了绝对控制。在IE6浏览器中,如果是用px为单位,那么在页面放大缩小的时候,就不会根据页面缩放的大小来调整基础文本的大小。当今所有主要浏览器(包括IE7和IE8)现在支持文本的像素单位 和/或 整版的缩放。在不考虑IE6的情况下,像素大小是首选。此外,没有单位的行高(line-height)是首选的,因为它不继承它的父元素一个百分比值,而是基于一个乘数的字体大小。
正确的写法:
错误的写法:
IE的那些BUG
不可避免的,当其他浏览器都工作正常时,IE的各个版本有可能会引入一些荒谬的BUG,搞得你措手不及。最后我们只好用CSS hooks加一些条件判断单独设置IE下的表现。更多详情请阅读paulirish的文章。
举例:
如果你使用HTML5(以及HTML5 Boilerplate),那推荐你使用Modernizer Javascript 库以及下面这个模式:
简写(CSS Shorthand)
一般来说,CSS简写是首选的,因为简洁。开发人员应该意识到TRBL(Top Right Bottom Left)缩略词,按顺时针“上、右、下、左”进行定义。例如:
padding-left:10px;padding-right:15px;padding-top:20px;padding-bottom:10px;
可将其简写为:padding:20px 15px 10px 10px;
参考阅读:
- http://qrayg.com/journal/news/css-background-shorthand
- http://sonspring.com/journal/css-redundancy
- http://dustindiaz.com/css-shorthand
图片
- 当需要使用重复平铺的图片时,图片尺寸大小应该大于 1x1像素。
- 注:链接给出了一个例子,他用了一个1×1的透明PNG图片作为div的重复背景,样式为:background:transparent url('images/transparent-bg.png') repeat left;,但发现在IE7和IE8下显示有问题,最后只好用了一个1×2的透明图片。
- 不要使用Spacer Images(空白图片)!
- 注:spacer image,指的是1×1的透明图片,用以填充表格的空白区域,以便撑住表格不变形。
- 请大量使用CSS sprites技术!这样做可以大大的减少图片的HTTP请求次数,从而加快网页的加载时间。
- 所有的切片图像应具有透明背景(PNG8)。切图应该紧着图片边缘切。
- 但是 logo 图片文件应该在切图时保留一个padding,这样便于其他人加热链。
文本和字体样式
标题
- 定义标题的默认样式为h1~h6。推荐做法是,在你的CSS文件顶部声明这些默认样式,必要时修改它们使得它们在网站中保持一致性。
- 标题的显示根据页面层级的不同和标题的重要性,从顶部开始,自上而下。h1为最大字体,h6为最小字体。
- SEO提示:请务必保证你的页面在失去CSS支持的时候,依然能够保证一个清晰的一个页面结构,因为搜索引擎对一个结构清晰的页面会特别关照。
链接
应确保链接的默认样式与主要文本的样式有所区分,鼠标悬停状态的样式也应该有所区分。
JavaScript
JavaScript是web页面中的第三个主要的组件。
JavaScript库
目前,我们主要使用jQuery开发新的web应用程序。
一般的编码规则
- 99%的代码应该放在外联JavaScript文件里。这些外联JavaScript文件的声明都应该放在body标记的底部,即</body>标签之前。
- 不要依赖于User-Agent字符串。一定要做适当的特征检测。(更多阅读:Dive Into HTML5: Detection & jQuery.support docs)。
- 不要使用document.write()!
- 所有的布尔变量应该用“is”开头。
- 注意变量和函数名称的命名逻辑:例如:popUpWindowForAd,而不是mywindow。
- 不要人为的简写变量名称。(除了在做For循环中传统的 i 变量),变量名应该是足够长以确保有意义。
- 你的文档应该遵循NaturalDocs结构。
- 常量或配置变量(如动画持续时间等),应在文件的顶部声明。
- 多使用函数,这些函数带有参数和返回值。函数中只存在抽象的逻辑代码。通过参数的传递使其该函数能够达到最大化的重用。当功能和代码逻辑需要改变的时候,可以大大的减少维护代码的开销。例如,我们需要做一个弹窗,弹窗内容、尺寸大小、URL链接,我们应该将其做为变量传递,而不是将这些属性值写死。
- 注释你的代码!它有助于花费更少的时间解决所遇到的问题。(让combo handler负责minify!)
- 内嵌的Javascript代码块,请使用<!-- -->包裹,除非你关心Netscape 4。
- 尽可能少地使用全局变量,如果你要使用,请给予一个命名空间!
合理使用空格
一般来说,使用空格应该遵循长期以来的英语阅读约定,符号左右应该存在一个空格,我们提倡代码的可读性。此外,前括号应该和总是和他的函数名处在同一行。
考虑下面这个例子,一个JavaScript循环…
变量、ID和类
所有JavaScript的变量名,都应该写成全部字母小写或者驼峰式大小写的形式。一个例外是,构造函数需要按照传统统统大写。所有CSS的ID和Class应该全部使用小写。尽可能的避免使用中划线和下划线。
事件委托
事件委托就是在一个页面上使用一个事件来管理多种类型的事件。这并不是一个新的想法,但对于把握性能来说却很重要。
事件委托对于web应用程序的性能有如下几个优点:
- 需要管理的函数变少了
- 占用的内存少了
- JavaScript代码和DOM结构之间的关联更少了
- 在改变DOM结构中的innerHTML时,不需要改动事件处理函数
从传统的事件处理方法转向事件委托,提高了大型web应用的性能。正因为它如此重要,一些类似于YUI、jQuey的javascript库也开始将事件委托应用在它们的核心接口中。实现事件委托是很轻松的,却能带来性能上很大的提高。尤其表现在你将数十个事件处理函数整合到一个函数里。试一下事件委托,你就不会再使用传统的事件处理方法了。
出于性能考虑,jQuery中的delegate()要大大优于live()。
调试
即使最好的验证器,也不可避免地会导致浏览器怪异模式的问题。有几个宝贵的工具,将有助于改进代码的完整性和加载速度。我们建议开发时先以Firefox和Safari第一,然后Chrome和Opera,最后有条件地单独调整Internet Explorer。
下面列出了有用的调试器和速度分析…
- Firefox: Firebug, Page Speed, YSlow
- Safari: Web Inspector
- Google Chrome: Developer Tools
- Opera: Dragonfly
- Internet Explorer 6-7: Developer Toolbar
- Internet Explorer 8-10: Developer Tools
良好的Javascript编写模式
- 编写可维护的代码
- 单var模式(即用一个var语句声明多个变量,并以逗号分隔)
- 不增加内置的原型
- 避免隐含类型转换
- 避免使用eval()
- 数字转换请使用parseInt()
- 注意左花括号的位置。
- 构造函数名大写
- 记得写注释
- 避免声明void
- 如果可能的话,避免位运算符(Bitwise Operator)
性能
我们将继续关注网络的性能优化,它仍然同样的重要。一个网页可以做到最小的性能消耗及等待时间。以下部分说明了如何对一个网页进行优化,让更多的浏览者更加满意。
优化并交付CSS和Javascript
- 遵循雅虎的性能指南
- 使用smush.it对图像做无损压缩!
- 适当时机请缓存headers。
- 静态资源文件要考虑单独开辟无 cooike 的子域(避免Request携带大量cookies)!
- 避免内联的<script>块!
- CSS应该定义在文档的<head>中,javascript 则应在</body>前通过外部链接的方式引用。
- CSS和Javascript在应用程序发布前进行压缩,很多人使用YUI Compressor来进行压缩。
- CSS和Javascript在服务端都应该被gzip压缩。
- CSS和Javascript文件应该被合并在一起,目的是在页面加载过程中减少对HTTP请求的次数!
- 避免在HTML文档中的内联
<script>
块,他们会阻塞页面渲染和页面加载时间!
优化Javascript执行顺序
在一个页面加载中,通常等待执行的脚本有很多。基于用户的响应,这个顺序优先级应如下:
- 会改变页面可视形态的的Script,优先级应该绝对是第一位的。这包括任何字体调整,布局框变更或IE6 的pngfixes。
- 页面内容初始化。
- 页眉、导航栏和页脚的初始化。
- 事件监听处理程序。
- 第三方的插件或脚本。
合理利用CSS Sprites技术
CSS Sprites把一些基本的图像资源合并到一个图像文件中。通过CSS背景定位的方式来获取其中的某一个部分。这样做的目的同样是为了减少http的请求次数。以下是一个典型的CSS Sprites:
使用CSS Sprites 能有效降低页面引用资源数,从而加快页面加载。更多内容请阅读CSS-tricks.com的技术和概述。
许多开发人员会使用一个垂直排列内部图片元素的Sprites,这种垂直排列的Sprites应该<= 100px宽(高)。包含图标通常应该放在文件里的最边上,如列表项icon小图标。雅虎使用了一些像这样的Sprites。
另外一个要考虑的是不要使用过大的Sprites,超过1000px的任意一个方向的Sprites会占用相当大的内存空间。
图片格式的应用
- JPEG——这将应用在所有的摄影图像,如主页或者频道页面中的宣传图像产品图像,或任何要求具备非常高的颜色数的图像。
- PNG24——方便在PhotoShop中输出高色数和全面支持分级不透明的图像,相对而言,这是一个相当重的格式,可能会产生千字节容量,而且在IE6中需要执行一个pngfix,在这种情况下我们建议使用DD_belatedPNG script(一个使IE6兼容背景透明的JS)。在很多时候,我们会为IE6后备一个GIF图像来支持背景透明。因为如果在一个很大的格式为PIN24的Sprites中应用pngfixes 会使所有的pngfixes的速度变得非常的慢,性能消耗代价特别大。因此最好避免在Sprites使用PNG24格式。
- PNG8——256色的颜色可以捕获一个令人惊讶的多样性, PNG是一个很大的可压缩比GIF(使用pngcrush和pngquant的工具,如)。此格式几乎在所有的浏览器允许渐变的不透明度,但在IE6中,那些半透明的像素是100%透明。在许多情况下,这是足够的。它也不会需要一个pngfix的脚本来运行,所以它的速度进行了优化。
- 透明GIF 89a——GIF 89a中提供了灵活性,透明度和广泛的浏览器的支持,但没有渐变的不透明度,而且色数不能超过256色。根据我们的经验,色彩深度为64仍然提供了非常好的品质缩览图,并能保持同等的文件大小。所有低色数,如图标或专题图形图像应使用PNG8格式,因为它是这四个格式中最高效的。对于大多数网站上的图形,PNG8是我们的主要建议使用的图像格式。
高速缓存
对于静态内容,只要是合理的,我们都应该在本地缓存文件。应该被缓存的文件包括:
- CSS和JavaScript文件
- 产品图片
- 专题图形
- favicon.ico标志
- flash的.swf文件
- 宣传多媒体影像
如果你的服务器是Apache,使用ExpiresDefault指令设置相对于当前日期的过期时间。这个例子ExpiresDefault指令集的要求过期时间为1年。
Expires: Thu, 15 Apr 2015 20:00:00 GMT
缓存只适用于准确的URL,所以改变文件名将开始一个新的拷贝。许多开发人员在文件发布或构建过程中添加一个版本号或时间戳。每一次构建将开始一个全新的缓存的版本,你不用担心缓存不会过期。
有节制的使用IFRAME
创建IFRAME比创建任何其他类型的DOM元素(包括脚本和样式表)耗时多出1-2个数量级。
尽快产生 Window的onload事件是很重要的。这个事件会引发浏览器状态指示到达“完毕”状态,通知用户页面已经下载完毕。当onload事件被延迟,给用户的感受是页面变的更慢了。
Window的onload事件在所有的iframe和iframe内所有元素完全下载后才能触发。在 Safari 和 Chrome浏览器内,通过JavaScript动态设置iframe的src属性可以避免这种阻塞行为。
有人可能希望一个iframe有它自己单独的并发连接池,但是事实并不是这样。在所有主流浏览器中,连接池是被页面和iframe共享的。这意味着有可能一个iframe内的资源占据了所有可用的连接并阻塞了主页面的资源下载。如果iframe内的内容和主页面一样重要或者更重要,这是没有问题的。但如果iframe不太重要,iframe占用连接是不可取的。一个解决办法是在 更高级别的资源下载之后,动态设置iframe的src属性动态加载内容
美国10大网站中5个使用了iframe。大多数情况下,它们常用来加载广告。这是不幸的,但可以理解,因为使用iframe插入广告内容很容易实现。在许多情况下,iframe是合乎逻辑的 解决方案。但要记住他们会对你的页面产生性能影响。尽可能避免使用iframe,当必须使用时,要有节制的使用它们。
FLASH
在应用FLASH动画的地方,请备注上相应的HTML文本,以便于SEO搜索引擎的优化。SWFObject的初始化应该在文档加载完毕之后。不要使用内嵌的JavaScript输出SWFObject。
建立于客户间的成功指标。
这些指标不管从技术角度以及可测试性应该是合理的:
- 在页面没有任何缓存的情况下,页面的完全加载和初始化应该保证在5秒以内。
- 鼠标经过时的变化(如hover)应该保证在100毫秒以内完成。