浏览器环境

  1. 模块化
  2. DOM

模块化

AMD

[强制] 使用 AMD 作为模块定义。

解释:

AMD 作为由社区认可的模块定义形式,提供多种重载提供灵活的使用方式,并且绝大多数优秀的 Library 都支持 AMD,适合作为规范。

目前,比较成熟的 AMD Loader 有:

[强制] 模块 id 必须符合标准。

解释:

模块 id 必须符合以下约束条件:

  1. 类型为 string,并且是由 / 分割的一系列 terms 来组成。例如:this/is/a/module
  2. term 应该符合 [a-zA-Z0-9_-]+ 规则。
  3. 不应该有 .js 后缀。
  4. 跟文件的路径保持一致。

define

[建议] 定义模块时不要指明 iddependencies

解释:

在 AMD 的设计思想里,模块名称是和所在路径相关的,匿名的模块更利于封包和迁移。模块依赖应在模块定义内部通过 local require 引用。

所以,推荐使用 define(factory) 的形式进行模块定义。

示例:


define(
    function (require) {
    }
);

[建议] 使用 return 来返回模块定义。

解释:

使用 return 可以减少 factory 接收的参数(不需要接收 exports 和 module),在没有 AMD Loader 的场景下也更容易进行简单的处理来伪造一个 Loader。

示例:


define(
    function (require) {
        var exports = {};

        // ...

        return exports;
    }
);

require

[强制] 全局运行环境中,require 必须以 async require 形式调用。

解释:

模块的加载过程是异步的,同步调用并无法保证得到正确的结果。

示例:


// good
require(['foo'], function (foo) {
});

// bad
var foo = require('foo');

[强制] 模块定义中只允许使用 local require,不允许使用 global require

解释:

  1. 在模块定义中使用 global require,对封装性是一种破坏。
  2. 在 AMD 里,global require 是可以被重命名的。并且 Loader 甚至没有全局的 require 变量,而是用 Loader 名称做为 global require。模块定义不应该依赖使用的 Loader。

[强制] Package 在实现时,内部模块的 require 必须使用 relative id

解释:

对于任何可能通过 发布-引入 的形式复用的第三方库、框架、包,开发者所定义的名称不代表使用者使用的名称。因此不要基于任何名称的假设。在实现源码中,require 自身的其它模块时使用 relative id

示例:


define(
    function (require) {
        var util = require('./util');
    }
);

[建议] 不会被调用的依赖模块,在 factory 开始处统一 require

解释:

有些模块是依赖的模块,但不会在模块实现中被直接调用,最为典型的是 css / js / tpl 等 Plugin 所引入的外部内容。此类内容建议放在模块定义最开始处统一引用。

示例:


define(
    function (require) {
        require('css!foo.css');
        require('tpl!bar.tpl.html');

        // ...
    }
);

DOM

元素获取

[建议] 对于单个元素,尽可能使用 document.getElementById 获取,避免使用document.all

[建议] 对于多个元素的集合,尽可能使用 context.getElementsByTagName 获取。其中 context 可以为 document 或其他元素。指定 tagName 参数为 * 可以获得所有子元素。

[建议] 遍历元素集合时,尽量缓存集合长度。如需多次操作同一集合,则应将集合转为数组。

解释:

原生获取元素集合的结果并不直接引用 DOM 元素,而是对索引进行读取,所以 DOM 结构的改变会实时反映到结果中。

示例:


<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,除非预期是需要包含文本、注释和属性类型的节点。

样式获取

[建议] 获取元素实际样式信息时,应使用 getComputedStylecurrentStyle

解释:

通过 style 只能获得内联定义或通过 JavaScript 直接设置的样式。通过 CSS class 设置的元素样式无法直接通过 style 获取。

样式设置

[建议] 尽可能通过为元素添加预定义的 className 来改变元素样式,避免直接操作 style 设置。

[强制] 通过 style 对象设置元素样式时,对于带单位非 0 值的属性,不允许省略单位。

解释:

除了 IE,标准浏览器会忽略不规范的属性值,导致兼容性问题。

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 操作有助于提高性能。举一个简单的例子,构建一个列表。我们可以用两种方式:

  1. 在循环体中 createElement 并 append 到父元素中。
  2. 在循环体中拼接 HTML 字符串,循环结束后写父元素的 innerHTML。

第一种方法看起来比较标准,但是每次循环都会对 DOM 进行操作,性能极低。在这里推荐使用第二种方法。

DOM 事件

[建议] 优先使用 addEventListener / attachEvent 绑定事件,避免直接在 HTML 属性中或 DOM 的 expando 属性绑定事件处理。

解释:

expando 属性绑定事件容易导致互相覆盖。

[建议] 使用 addEventListener 时第三个参数使用 false

解释:

标准浏览器中的 addEventListener 可以通过第三个参数指定两种时间触发模型:冒泡和捕获。而 IE 的 attachEvent 仅支持冒泡的事件触发。所以为了保持一致性,通常 addEventListener 的第三个参数都为 false。

[建议] 在没有事件自动管理的框架支持下,应持有监听器函数的引用,在适当时候(元素释放、页面卸载等)移除添加的监听器。

0 条评论