為了提高頁面的性能,通常情況下,我們希望資源盡可能地早地並行加載。這里有兩個要點,首先是盡早,其次是並行。
通過data-main
方式加載要盡可能地避免,因為它讓requirejs、業務代碼不必要地串行起來。下面就講下如何盡可能地利用瀏覽器並行加載的能力來提高性能。
低效串行:想愛但卻無力
最簡單的優化,下面的例子中,通過兩個並排的script標簽加載require.js、main.js,這就達到了require.js、main.js並行加載的目的。
但這會有個問題,假設main.js依賴了jquery.js、anonymous.js(如下代碼所示),那么,只有等main.js加載完成,其依賴模塊才會開始加載。這顯然不夠理想,后面我們會講到如何避免這種情況,下面是簡單的源碼以及效果示意圖。
demo.html
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>main.js、anynomous.js串行加載</h1>
<script type="text/javascript" src="js/require.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
js/main.js:
require(['js/anonymous'], function(Anonymous) {
alert('加載成功');
});
js/anonymous.js:
define(['js/jquery'], function() {
console.log('匿名模塊,require直接報錯了。。。');
return{
say: function(msg){
console.log(msg);
}
}
});
最終效果:
簡單匿名:一條走不通的路
正常情況下,假設頁面里有如下幾個<script>
標簽,現代瀏覽器就會並發請求文件,並順序執行。但在requirejs里,如果這樣做的話,可能會遇到一些意料之外的情況。如下所示,四個並排的標簽,依次請求了require.js
、jquery.js
、anonymous.js
、main.js
。
demo.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<h1>requirejs並行加載例子</h1>
<script type="text/javascript" src="js/require.js"></script>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/anonymous.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
預期中,資源會並行加載,但實際上,你會在控制台里看到下面的錯誤日志。
為什么呢?對於requirejs來說,上面的js/anonymous.js
是一個匿名的模塊,requirejs對它一無所知。當你在main中告訴requirejs說我要用到js/anonymous
這個模塊時,它就傻眼了。所以,這里就直接給你報個錯誤提個醒:不要這樣寫,我不買賬。
那么,及早並行加載的路是否走不通了呢?未必,請繼續往下看。
答案就在身邊:注冊為命名模塊的jquery
簡單改下上面的例子,比如這樣,然后。。它就行了。。
<script type="text/javascript" src="js/require.js"></script>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/main.js"></script>
原因很簡單。因為jquery把自己注冊成了命名模塊。requirejs於是就認得jquery了。
if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
define( "jquery", [], function () { return jQuery; } );
}
jquery的啟發:起個好名字很重要
上面我們看到,給模塊起個名字,將匿名模塊改成命名模塊(named module),就開啟了我們的並行加載之旅。從這點看來,起名字真的很重要。
那么我們對之前的例子進行簡單的改造。這里用了個小技巧,利用命名模塊js/name-module.js
來加載之前的匿名模塊js/anonymous.js
。可以看到,requirejs不報錯了,requirejs跟name-module.js也並行加載了。
demo.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<h1>並行加載requirejs、jquery</h1>
<script type="text/javascript" src="js/require.js"></script>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/name-module.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
js/name-module.js
define('name-module', 'js/anonymous', [], function() {
return {
say: function(msg){
alert(msg);
}
};
});
最終效果圖:
通往希望之門:解決anonymous模塊的串行問題
如果你能耐着性子看到這一節,說明少年你已經發現了上一節很明顯的一個問題:盡管name-module.js並行加載了,但anonymou.js其實還是串行加載,那做這個優化還有什么意義?
沒錯,如果最終優化效果這樣的話,那是完全無法接受的。不賣關子,這個時候就要請出我們的requirejs打包神器r.js
。通過打包優化,將anonymous.js
、name-module.js
打包生成一個文件,就解決了串行的問題。
1、安裝打包工具
npm install -g requirejs
2、創建打包配置文件,注意,由於jquery.js比較通用,一般情況下會單獨加載,所以從打包的列表里排除
{
"appDir": "./", // 應用根路徑
"baseUrl": "./", //
"dir": "dist", // 打包的文件生成到哪個目錄
"optimize": "none", // 是否壓縮
"modules": [
{
"name": "js/name-module",
"exclude": [
"jquery" // 將jqury從打包規則里排除
]
}
]
}
3、運行如下命令打包
r.js -o ./build.js
4、打包后的name-module
,可以看到,匿名模塊也被打包進去,同時被轉換成了命名模塊
define('js/anonymous',['jquery'], function() {
console.log('匿名模塊,require直接報錯了。。。');
return{
say: function(msg){
console.log('anonymous: '+msg);
}
}
});
define('js/name-module', ['js/anonymous'], function() {
return {
say: function(msg){
alert('name module: '+msg);
}
};
});
5、再次訪問demo.html,很好,就是我們想要的結果
寫在后面
上面主要提供了及早並行加載的思路,但在實際利用requirejs打包的過程中,還會遇到一些需要小心處理的細節問題,當然也有一些坑。后面有時間再總結一下。