这是2013年的文章,那时候使用RequireJS进行异步加载是优化网站性能的流行做法,到了现在(2026),异步加载还是需要,但是完全就没有必要使用RequireJS了,本站就是自己实现的异步加载功能。
在文章开始之前,先说一个当时遇到的实际问题。
手里有一个网站,前端的大部分 JavaScript 效果都是使用 jQuery 编写的。由于我本身比较关注 SEO 和网站加载速度,所以对网站性能一直比较在意。
以前其实并没有特别关注过这个问题,一方面是自己的网络速度还算快,另一方面可能也是当时对前端性能了解得还不够深入。
后来偶然使用 Google PageSpeed 对网站进行了测试,发现评分并不理想,其中比较明显的问题主要集中在:
- JavaScript
- 图片
图片优化这里暂时不讨论,这篇主要记录一下当时遇到的 JavaScript 加载问题,以及后来使用 RequireJS 的处理方法。
jQuery 加载带来的问题
当时网站的 <head> 中直接加载了 jQuery:
<script src="jquery.js"></script>
由于页面中后面的很多脚本都依赖 jQuery,因此必须等 jQuery 下载并执行完成之后,相关代码才能继续正常运行。
对于页面来说,这也可能影响后续内容的加载和渲染。
于是,我首先想到的是使用浏览器提供的异步加载方式,例如:
<script src="jquery.js"async></script>
或者:
<script src="jquery.js"defer></script>
这样确实可以让 jQuery 不再按照传统方式阻塞脚本加载。
但很快又遇到了另外一个问题。
异步加载后,依赖关系怎么办?
网站中很多代码都是使用 jQuery 编写的,例如:
$(".menu").hide();或者:
$("#submit").click(function(){
// ...
});如果 jQuery 异步加载,而这些依赖 jQuery 的脚本已经开始执行,就可能出现:
也就是说,虽然 jQuery 和其他 JavaScript 都进行了异步加载,但:
谁先加载完成,其实是不可控的。
例如:
因此,单纯把所有 JavaScript 都改成异步加载,并不能解决模块之间的依赖问题。
当时由于需要尽快解决这个问题,最后先采用了一个比较简单的办法:
把 jQuery 和依赖 jQuery 的脚本统一放到页面底部加载。
虽然这个办法比较直接,但至少能够保证页面主体内容先完成加载,再执行相关 JavaScript。
不过,后来了解到 RequireJS 之后,发现它正好可以解决 JavaScript 的模块依赖和异步加载问题。
什么是 RequireJS?
这里不准备讲太多概念。
对我来说,RequireJS 最主要的作用就是解决两个问题:
- JavaScript 模块的异步加载
- 模块之间的依赖关系管理
例如,一个脚本依赖 jQuery,那么就明确告诉 RequireJS:
这个脚本需要先加载 jQuery,等 jQuery 准备好之后再执行。
这样就不需要自己去判断 jQuery 到底什么时候加载完成。
基本加载方式
首先在页面中加载 RequireJS:
<script
src="require.js"
data-main="main"
async>
</script>
其中:
表示 RequireJS 会继续加载对应的:main.js
main.js 主要负责后续模块的配置和加载。
页面结构可以简单理解为:
配置 jQuery 模块
main.js 的内容可以这样写:
require.config({
paths: {
"jquery": "http://code.jquery.com/jquery-1.10.2.min"
}
});这里的:
用于配置模块路径。
上面的配置表示:
这个模块对应:jquery-1.10.2.min.js
当然,也可以把 jQuery 放在自己的服务器上,例如:
require.config({
paths: {
"jquery": "/js/jquery-1.10.2.min"
}
});这里需要注意一点,配置模块路径时通常不需要填写:
后缀。
也就是说:"jquery": "/js/jquery-1.10.2.min"
而不是:"jquery": "/js/jquery-1.10.2.min.js"
加载 jQuery 并执行代码
配置完成之后,可以通过:
require(['jquery'], function($) {
$("title").text("网页的标题被 jQuery 改变了");
});来加载 jQuery。
这段代码可以理解为:
先加载 jquery 模块,等模块加载完成之后,再执行后面的函数。
函数中的:
就是加载完成后的 jQuery 对象。
因此,RequireJS 会处理加载顺序:
这样就避免了因为异步加载顺序不确定,而出现:
的问题。
页面加载速度和模块依赖
使用 RequireJS 之后,JavaScript 可以按照模块的方式进行加载。
页面不需要一次性加载所有脚本,而是根据实际需要加载对应的模块。
例如:
对于一些功能比较多的网站来说,这种方式可以让 JavaScript 的组织结构更加清晰。
同时,模块之间的依赖关系也不需要完全依靠手动控制。
例如:
require(['jquery', 'moduleA'], function($, moduleA) {
// jQuery 和 moduleA 都加载完成后执行
});只有需要的模块准备完成之后,相关代码才会执行。
当时的一点理解
当时接触 RequireJS 之后,感觉它最大的价值并不仅仅是“异步加载”。
真正解决的问题其实是:
异步加载之后,如何正确处理 JavaScript 之间的依赖关系。
如果只是简单使用 async 加载多个脚本,虽然它们都可以异步下载,但执行顺序并不一定符合实际需求。
而模块化加载可以明确告诉程序:
对于当时依赖大量 jQuery 脚本的网站来说,这种方式确实方便了很多。
当然,如果只是一个 JavaScript 文件不多的小型网站,直接把 jQuery 和相关代码放在页面底部加载,也是一种简单有效的处理方式。
不同的网站,还是应该根据实际情况选择合适的加载方案。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。