转载:原地址
4 浏览器环境
4.1 模块化
4.1.1 AMD
[强制] 使用 AMD
作为模块定义。
解释:
AMD 作为由社区认可的模块定义形式,提供多种重载提供灵活的使用方式,并且绝大多数优秀的 Library 都支持 AMD,适合作为规范。
目前,比较成熟的 AMD Loader 有:
[强制] 模块 id
必须符合标准。
解释:
模块 id 必须符合以下约束条件:
- 类型为 string,并且是由
/
分割的一系列 terms 来组成。例如:this/is/a/module
。 - term 应该符合 [a-zA-Z0-9_-]+ 规则。
- 不应该有 .js 后缀。
- 跟文件的路径保持一致。
4.1.2 define
[建议] 定义模块时不要指明 id
和 dependencies
。
解释:
在 AMD 的设计思想里,模块名称是和所在路径相关的,匿名的模块更利于封包和迁移。模块依赖应在模块定义内部通过 local require 引用。
所以,推荐使用 define(factory) 的形式进行模块定义。
示例:
javascript
define( function (require) { } );
[建议] 使用 return
来返回模块定义。
解释:
使用 return 可以减少 factory 接收的参数(不需要接收 exports 和 module),在没有 AMD Loader 的场景下也更容易进行简单的处理来伪造一个 Loader。
示例:
javascript
define( function (require) { var exports = {}; // ... return exports; } );
4.1.3 require
[强制] 全局运行环境中,require
必须以 async require
形式调用。
解释:
模块的加载过程是异步的,同步调用并无法保证得到正确的结果。
示例:
javascript
// good require(['foo'], function (foo) { }); // bad var foo = require('foo');
[强制] 模块定义中只允许使用 local require
,不允许使用 global require
。
解释:
- 在模块定义中使用 global require,对封装性是一种破坏。
- 在 AMD 里,global require 是可以被重命名的。并且 Loader 甚至没有全局的 require 变量,而是用 Loader 名称做为 global require。模块定义不应该依赖使用的 Loader。
[强制] Package在实现时,内部模块的 require
必须使用 relative id
。
解释:
对于任何可能通过 发布-引入 的形式复用的第三方库、框架、包,开发者所定义的名称不代表使用者使用的名称。因此不要基于任何名称的假设。在实现源码中,require 自身的其它模块时使用 relative id。
示例:
javascript
define( function (require) { var util = require('./util'); } );
[建议] 不会被调用的依赖模块,在 factory
开始处统一 require
。
解释:
有些模块是依赖的模块,但不会在模块实现中被直接调用,最为典型的是 css / js / tpl 等 Plugin 所引入的外部内容。此类内容建议放在模块定义最开始处统一引用。
示例:
javascript
define( function (require) { require('css!foo.css'); require('tpl!bar.tpl.html'); // ... } );
4.2 DOM
4.2.1 元素获取
[建议] 对于单个元素,尽可能使用 document.getElementById
获取,避免使用document.all
。
[建议] 对于多个元素的集合,尽可能使用 context.getElementsByTagName
获取。其中 context
可以为 document
或其他元素。指定 tagName
参数为 *
可以获得所有子元素。
[建议] 遍历元素集合时,尽量缓存集合长度。如需多次操作同一集合,则应将集合转为数组。
解释:
原生获取元素集合的结果并不直接引用 DOM 元素,而是对索引进行读取,所以 DOM 结构的改变会实时反映到结果中。
示例:
html
<div></div> <span></span> <script> var elements = document.getElementsByTagName('*'); // 显示为 DIV alert(elements[0].tagName); var div = elements[0]; var p = document.createElement('p'); docpment.body.insertBefore(p, div); // 显示为 P alert(elements[0].tagName); </script>
[建议] 获取元素的直接子元素时使用 children
。避免使用childNodes
,除非预期是需要包含文本、注释和属性类型的节点。
4.2.2 样式获取
[建议] 获取元素实际样式信息时,应使用 getComputedStyle
或 currentStyle
。
解释:
通过 style 只能获得内联定义或通过 JavaScript 直接设置的样式。通过 CSS class 设置的元素样式无法直接通过 style 获取。
4.2.3 样式设置
[建议] 尽可能通过为元素添加预定义的 className 来改变元素样式,避免直接操作 style 设置。
[强制] 通过 style 对象设置元素样式时,对于带单位非 0 值的属性,不允许省略单位。
解释:
除了 IE,标准浏览器会忽略不规范的属性值,导致兼容性问题。
4.2.4 DOM 操作
[建议] 操作 DOM
时,尽量减少页面 reflow
。
解释:
页面 reflow 是非常耗时的行为,非常容易导致性能瓶颈。下面一些场景会触发浏览器的reflow:
- DOM元素的添加、修改(内容)、删除。
- 应用新的样式或者修改任何影响元素布局的属性。
- Resize浏览器窗口、滚动页面。
- 读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE)) 。
[建议] 尽量减少 DOM
操作。
解释:
DOM 操作也是非常耗时的一种操作,减少 DOM 操作有助于提高性能。举一个简单的例子,构建一个列表。我们可以用两种方式:
- 在循环体中 createElement 并 append 到父元素中。
- 在循环体中拼接 HTML 字符串,循环结束后写父元素的 innerHTML。
第一种方法看起来比较标准,但是每次循环都会对 DOM 进行操作,性能极低。在这里推荐使用第二种方法。
4.2.5 DOM 事件
[建议] 优先使用 addEventListener / attachEvent
绑定事件,避免直接在 HTML 属性中或 DOM 的 expando
属性绑定事件处理。
解释:
expando 属性绑定事件容易导致互相覆盖。
[建议] 使用 addEventListener
时第三个参数使用 false
。
解释:
标准浏览器中的 addEventListener 可以通过第三个参数指定两种时间触发模型:冒泡和捕获。而 IE 的 attachEvent 仅支持冒泡的事件触发。所以为了保持一致性,通常 addEventListener 的第三个参数都为 false。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。