標籤:
筆者閱讀了園友艾倫 Aaron的系列部落格《jQuery源碼分析系列》,主要是閱讀的jQuery的原理,然後跑園友的代碼,真正對jQuery源碼的閱讀並不多。主要是直接閱讀jQuery源碼,一次能讀懂的部分並不多,不如先閱讀源碼解析的文章,然後再閱讀收穫更大。筆者在閱讀完園友艾倫的全部jQuery源碼部落格之後,準備真正開始閱讀jQuery的原始碼。而閱讀的形式屬於自問自答,把在閱讀部落格時候的留下來的疑問提出來,再通過自己閱讀源碼的形式對其解答;當然也包括在閱讀原始碼的時候那些讀不懂的地方,提出疑問,然後在慢慢消化解答。
這個jQuery源碼系列並非教程,而是筆者的學習jQuery源碼的筆記,因此才會採取問答這種不系統的形式。如果大家想系統學習jQuery源碼,還是請仔細閱讀艾倫 Aaron的部落格;或是直接閱讀jQuery在gitbub上的源碼(目前的master版本是version to 3.1.1-pre.,筆者閱讀的也是這個版本的代碼)。如果在閱讀後也和筆者一樣,對一些內容存在疑問,可以看看筆者的系列部落格,看看你有沒有你疑惑的問題。
提問:如何構建jQuery的源碼
在我開始閱讀jQuery的源碼時,發現原始碼本身是有模組化的,而艾倫 Aaron閱讀的版本沒有,他是直接閱讀的dist後的代碼。那麼jQuery是如何構建原始碼的呢?
答:閱讀jQuery在github上的README.md檔案,裡面已經說得非常清楚了。先安裝git用戶端和nodejs及npm。然後將github上的資源複製到本地,找一個目錄,然後開啟命令列,輸入git的clone命令:
git clone git://github.com/jquery/jquery.git
本地就會複製出jQuery的原始碼:
然後進入該目錄下,在命令列上運行如下命令,jQuery就會自動安裝npm模組,並構建jQuery了。
npm run build
最後在dist目錄中,會構建出我們想要的jQuery檔案了。
提問:git上的jQuery目錄結構是怎樣的
答:具體目錄結構可以看,這裡逐個介紹一下各個目錄和檔案的作用:
| .github |
git相關目錄 |
| build |
構建相關的指令檔,筆者大概閱讀了一下,主要是建立兩個Grunt任務,負責構建(bulid)和輸出(dist) |
| dist |
輸出構建後的檔案,主要是jquery.js和jquery.min.js,以及jquery.min.map。這些就是我們最終使用的js檔案了 |
| external |
放依賴的第三方代碼,jQuery中最重的第三方庫sizzle就在這裡
|
| node_modules |
nodejs的模組檔案,構建時候安裝的node非全域模組都放在這裡,屬性nodejs的小夥伴應該很清楚了 |
| src |
jQuery自身的原始碼的目錄,這就是我們主要閱讀的部分 |
| test |
單元測試用例 |
| .babelrc |
Babel的設定檔 |
| .editorconfig |
editorconfig的設定檔,editorconfig是協助開發人員在不同的編輯器和IDE之間定義和維護一致的代碼風格的配置,很多ide都支援editorconfig的外掛程式,使用這個就可以使各種ide下都能自動將代碼顯示成editorconfig約定的格式 |
| .eslintignore |
eslint相關檔案,配置不用於eslint文法檢測的檔案目錄 |
| .eslintrc.json |
eslint的設定檔。eslint和jshint比較像,可以認為是jshint的升級版,都是用來做文法檢測的。 |
| .gitattributes |
git相關檔案,用於設定檔案的對比方式 |
| .gitignore |
git的設定檔,配置用於配置不需要加入版本管理的檔案 |
| .mailmap |
貌似是一個郵件地址清單 |
| .npmignore |
npm相關檔案,配置哪些檔案不需要被發布到npmjs.org |
| .npmrc |
npm的設定檔 |
| .travis.yml |
Tarvis-CI的設定檔,持續整合用的,github整合了持續整合服務travis,每一次push之後,travis就會定時執行“npm test”來測試專案 |
| AUTHORS.txt |
貢獻者名錄 |
| CONTRIBUTING.md |
貢獻代碼指引 |
| Gruntfile.js |
Grunt的設定檔 |
| LICENSE.txt |
授權協議 |
| README.md |
github項目的說明文檔 |
| package.json |
npm的包檔案 |
幾個地方值得注意一下:
1.jQuery是用RequireJs做模組化工具的,所以jQuery的原始碼是用AMD規範。AMD這個模組化規範主要活躍於基於瀏覽器的js模組管理,他有著非同步載入、依賴前置等非常好的特性,當然用來做後端的代碼模組管理也可以,但是筆者終究認為這裡用AMD有點“大材小用”了。jQuery用AMD規範做模組化管理,可能是曆史原因的影響,早些年模組化工具不像如今這麼成熟。
2.jQuery是用Grunt構建的,jQuery自己做了構建的Grunt任務,具體代碼可以參考build目錄下的Grunt任務和Grunt的設定檔Gruntfile.js。基於Grunt,jQuery完成測試、構建、合并代碼、壓縮等工作,最終把構建好的檔案拷貝到dist目錄裡面。
3.jQuery配置了npm包相關的配置,說明jQuery本身也被發布到了npmjs.org上,我們可以直接用npm安裝jQuery。
4.jQuery有Babel的設定檔,那原始碼是不是用ES6的文法呢?
提問:jQuery有Babel的設定檔,難道jQuery的原始碼使用了ES6的文法?
答:答案是沒有的,我們先不看Babel在Grunt裡面的配置,直接試一下能不能在原始碼中使用ES6。
先將原始碼裡面添加一條ES6的文法,然後執行構建。這時我們會發現構建的時候會報錯,而且構建好的jQuery.js檔案中ES6的文法也沒有被轉換為ES5的文法。這說明構建原始碼的時候並沒有啟用Babel。
我們在來看Babel在Grunt裡面的配置
babel: { options: { sourceMap: "inline", retainLines: true }, nodeSmokeTests: { files: { "test/node_smoke_tests/lib/ensure_iterability.js": "test/node_smoke_tests/lib/ensure_iterability_es6.js" } }},
可以看出,Babel僅是將測試案例中的煙霧測試 (Smoke Test)用例做了轉換,而原始碼則不在被轉換列表中。
提問:jQuery的原始碼用的是AMD規範做模組化,為什麼最終構建好的代碼卻沒有AMD的語句呢?
答:閱讀jQuery的原始碼,我們可以很清楚的看到AMD規範的語句。
然後在dist裡面的構建好的檔案中,我們卻找不到AMD規範語句的影子,jQuery是如何去掉了這些AMD語句呢?起初筆者認為是用了一些Grunt上的外掛程式,後來閱讀了build裡面的代碼才發現,是在build裡面使用正則將AMD語句去除。jQuery雖然是用的AMD做模組化,但是具體的代碼結構還是有自己的一套策略,這個應該就是為了最後構建代碼而考慮的。
src的根目錄下,共有20多個js檔案,很多js檔案都有同名的目錄與之對應。目錄之中最特別的目錄就是var目錄,因為var目錄並不存在與之對應的名為var.js的檔案。
var這個名字和js的關鍵字var相同,這和這個目錄這樣命名肯定是有關係的。沒錯,jQuery在去除AMD語句的時候,發現如果是var目錄裡的檔案的話,就會將其轉換為“var 檔案名稱 = AMD輸出的對象”的形式。如:
define( ‘var/arr‘,[],function() { "use strict"; return [];} );define( ‘var/document‘,[],function() { "use strict"; return window.document;} );define( ‘var/getProto‘,[],function() { "use strict"; return Object.getPrototypeOf;} );......
原始碼的這幾個模組最終會把構建為:
var arr = [];var document = window.document;var getProto = Object.getPrototypeOf;
......
而對於非“var”目錄下的模組,jQuery會將define關鍵字等AMD相關的語句直接移除。其中sizzle模組更為特殊,jQuery在構建的時候還做了更多的操作,這裡就不再細究了。
分析到這大家也能看出,jQuery這樣構建是會破壞AMD各個模組的範圍的。我們之所以需要模組化我們的代碼,一點是不希望我們把我們的內部(私人)變數聲明到全域範圍中,而JavaScript是一種函數級範圍的語言,jQuery在構建的時候去掉了AMD的函數形式,這樣所有模組的代碼構建後的都會在同一個範圍裡,所以在修改jQuery原始碼的時候一定要小心,不要在範圍裡面隨意建立相同名稱的變數,防止造成模組間的衝突。
以上就是jQuery去除AMD的過程。
瞭解了jQuery的構建過程後,我們就可以輕鬆愉快地瀏覽jQuery的原始碼了。事實上如果jQuery構建的時候採用webpack這樣的工具就簡單多了。jQuery沒有採用這樣的工具可能也是曆史原因,或者是出於進一步最佳化代碼的目的。
問答形式閱讀jQuery源碼(一)