跟我学习javascript的异步脚本加载
前端之家收集整理的这篇文章主要介绍了
跟我学习javascript的异步脚本加载,
前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
先来看这行代码:
这有点儿……不怎么样。“这该放在哪儿?”开发人员会奇怪,“靠上点,放到
标签里?还是靠下点,放到
标签里?”这两种做法都会让富脚本
站点的下场很凄惨。
标签里的大脚本会滞压所有
页面渲染工作,使得
用户在脚本加载完毕之前一直处于“白屏死机”状态。而
标签末尾的大脚本只会让
用户看到毫无生命力的静态
页面,原本应该进行客户端渲染的地方却散布着不起作用 的控件和空空如也的方框。
完美解决这个问题需要对脚本分而治之:那些负责让页面更好看、更好用的脚本应该立即加载,而那些可以待会儿再加载的脚本稍后再加载。但是怎样才能既滞压这些脚本,又能保证它们在被调用时的可用性呢?
一、
这确实大大缩短了加载时间,但要注意一点,这可能让用户有机会在加载bodyScripts.js 之前与页面交互。
2、 脚本的提前加载与延迟运行
上面建议将大多数脚本放在
中,因为这样既能让
用户更快地看到网页,又能避免操控DOM之前绑定“就绪”事件的开销。但这种方式也有一个缺点,
即浏览器在加载完整个文档之前无法加载这些脚本,这对那些通过慢速连接传送的大型文档来说会是一大瓶颈。
理想情况下,脚本的加载应该与文档的加载同时进行,并且不影响DOM 的渲染。这样,一旦文档就绪就可以运行脚本,因为已经按照
请记住deferredScripts 的封装很重要,这样即使浏览器不支持defer,deferredScripts 也会在文档就绪事件之后才运行。如果页面主体内容远远超过几千字节,那么付出这点代价是完全值得的。
3、 脚本的并行加载
如果你是斤斤计较到毫秒级页面加载时间的完美主义者,那么defer也许就像是淡而无味的薄盐酱油。你可不想一直等到此前所有的defer 脚本都运行结束,当然也肯定不想等到文档就绪之后才运行这些脚本,你就是想尽快加载并且尽快运行这些脚本。这也正是现代浏览器提供了async(异步)属性的原因。
如果说defer 让我们想到一种静静等待文档加载的有序排队场景,那么async 就会让我们想到混乱的无政府状态。前面给出的那两个脚本会以任意次序运行,而且只要JavaScript 引擎可用就会立即运行,而不论文档就绪与否。
对大多数脚本来说,async 是一块难以下咽的鸡肋。async 不像defer那样得到广泛的支持。同时,由于异步脚本会在任意时刻运行,它实在太容易引起海森堡蚁虫之灾了(脚本刚好结束加载时就会蚁虫四起)。
当我们加载一些第三方脚本,而且也不在乎它们谁先运行谁后运行。因此,对这些第三方脚本使用async 属性,相当于一分钱没花就提升了它们的运行速度。
上一个页面示例再添加两个独立的第三方小部件,得到的结果如下:
很眼熟?这个结构和讨论defer 属性那一节给出的结构一样,唯一的区别是这里的某个脚本文件已经拼接了yepnope.js(很可能就在deferredScripts.js 的顶部),这样就可以独立地加载那些根据条件再加载的脚本(因为浏览器需要垫片脚本)和那些想要动态加载的脚本(以便回应用户的动作)。结果将是一个更小巧的deferredScripts.js。
四、Require.js/AMD 模块化加载
开发人员想通过脚本加载器让混乱不堪的富脚本应用变得更规整有序一些,而Require.js 就是这样一种选择。Require.js 这个强大的工具包能够
自动和AMD技术一起捋顺哪怕最复杂的脚本依赖图。
现在先来看一个用到Require.js 同名
函数的简单脚本加载示例。
require 函数接受一个由模块名称构成的数组,然后并行地加载所有这些脚本模块。与yepnope 不同,Require.js 不会保证按顺序运行目标脚本,只是保证它们的运行次序能满足各自的依赖性要求,但前提是
这些脚本的定义遵守了AMD(Asynchronous Module Definition,异步模块定义)规范。
案例一: 加载 JavaScript 文件
如案例一 所示,有两个 JavaScript 文件 a.js 和 b.js,里面各自定义了 myFunctionA 和 myFunctionB 两个方法,通过下面这个方式可以用 RequireJS 来加载这两个文件,在 function 部分的代码可以引用这两个文件里的方法。
require 方法里的这个字符串数组参数可以允许不同的值,当字符串是以”.js”结尾,或者以”/”开头,或者就是一个 URL 时,RequireJS 会认为用户是在直接加载一个 JavaScript 文件,否则,当字符串是类似”my/module”的时候,它会认为这是一个模块,并且会以用户配置的 baseUrl 和 paths 来加载相应的模块所在的 JavaScript 文件。配置的部分会在稍后详细介绍。
这里要指出的是,RequireJS 默认情况下并没有保证 myFunctionA 和 myFunctionB 一定是在页面加载完成以后执行的,在有需要保证页面加载以后执行脚本时,RequireJS 提供了一个独立的 domReady 模块,需要去 RequireJS 官方网站下载这个模块,它并没有包含在 RequireJS 中。有了 domReady 模块,案例一 的代码稍做修改加上对 domReady 的依赖就可以了。
案例二: 页面加载后执行 JavaScript
执行案例二的代码后,通过 Firebug 可以看到 RequireJS 会在当前的页面上插入为 a.js 和 b.js 分别声明了一个 < script> 标签,用于异步方式下载 JavaScript 文件。async 属性目前绝大部分浏览器已经支持,它表明了这个 < script> 标签中的 js 文件不会阻塞其他页面内容的下载。
案例三:RequireJS 插入的 < script>
AMD推行一个由Require.js 负责提供的名叫define 的全局函数,该函数有3 个参数:
- 模块名称,
- 模块依赖性列表,
- 在那些依赖性模块加载结束时触发的回调。
使用 RequireJS 来定义 JavaScript 模块
这里的 JavaScript 模块与传统的 JavaScript
代码不一样的地方在于它无须访问全局的变量。模块化的设计使得 JavaScript
代码在需要访问”
全局变量”的时候,都可以通过依赖关系,把这些”
全局变量”作为参数传递到模块的实现体里,在实现中就避免了访问或者声明全局的变量或者
函数,有效的避免大量而且复杂的命名空间管理。
如同 CommonJS 的 AMD 规范所述,定义 JavaScript 模块是通过 define
方法来实现的。
下面我们先来看一个简单的例子,这个例子通过定义一个 student 模块和一个 class 模块,在主程序中实现创建 student 对象并将 student 对象放到 class 中去。
案例四: student 模块,student.js
案例五:class 模块,class.js
案例六: 主程序
输出 2
});
student 模块和 class 模块都是独立的模块,下面我们再定义一个新的模块,这个模块依赖 student 和 class 模块,这样主程序部分的逻辑也可以包装进去了。
案例七:依赖 student 和 class 模块的 manager 模块,manager.js
案例八:新的主程序
输出 2
});
通过上面的代码示例,我们已经清楚的了解了如何写一个模块,这个模块如何被使用,模块间的依赖关系如何定义。
其实要想让自己的站点更快捷,可以异步加载那些暂时用不到的脚本。为此最简单的做法是审慎地使用defer 属性和async 属性。如果要求根据条件来加载脚本,请考虑像yepnope 这样的脚本加载器。如果站点存在大量相互依赖的脚本,请考虑Require.js。选择最适合任务的工具,然后使用它,享受它带来的便捷。
以上就是关于javascript的异步脚本加载的全部内容,想对大家的学习有所帮助。