GitHub.com 在 2007 年底開始使用 jQuery 1.2.1,那是谷歌發(fā)布 Chrome 瀏覽器的前一年。當時還沒有通過 CSS 選擇器來查詢 DOM 元素的標準方法,也沒有動態(tài)渲染元素的樣式的標準方法,而 Internet Explorer 的 XMLHttpRequest 接口與其他很多 API 一樣,在瀏覽器之間存在不一致性問題。
jQuery 讓 DOM 操作、創(chuàng)建動畫和“AJAX”請求變得相當簡單,基本上,它讓 Web 開發(fā)人員能夠創(chuàng)建更加現(xiàn)代化的動態(tài) Web 體驗。最重要的是,使用 jQuery 為一個瀏覽器開發(fā)的代碼也適用于其他瀏覽器。在 GitHub 的早期階段,jQuery 讓小型的開發(fā)團隊能夠快速進行原型設(shè)計并開發(fā)出新功能,而無需專門針對每個 Web 瀏覽器調(diào)整代碼。
基于 jQuery 簡單的接口所構(gòu)建的擴展庫也成為 GitHub.com 前端的基礎(chǔ)構(gòu)建塊:pjax(https://github.com/defunkt/jquery-pjax)和 facebox(https://github.com/defunkt/facebox)。
我們將永遠不會忘記 John Resig 和 jQuery 貢獻者創(chuàng)建和維護的這樣一個有用的基本庫。
多年來,GitHub 成長為一家擁有數(shù)百名工程師的公司,并逐漸成立了一個專門的團隊,負責(zé) JavaScript 代碼的規(guī)模和質(zhì)量。我們一直在排除技術(shù)債務(wù),有時技術(shù)債務(wù)會隨著依賴項的增多而增長,這些依賴項在一開始會為我們帶來一定的價值,但這些價值也隨著時間的推移而下降。
我們可以將 jQuery 與現(xiàn)代瀏覽器支持的 Web 標準的快速演化進行比較:
$(selector) 模式可以使用 querySelectorAll() 來替換;
現(xiàn)在可以使用 Element.classList 來實現(xiàn) CSS 類名切換;
CSS 現(xiàn)在支持在樣式表中而不是在 JavaScript 中定義可視動畫;
現(xiàn)在可以使用 Fetch Standard 執(zhí)行 $.ajax 請求;
addEventListener() 接口已經(jīng)足夠穩(wěn)定,可以跨平臺使用;
我們可以使用輕量級的庫來封裝事件委托模式;
隨著 JavaScript 語言的發(fā)展,jQuery 提供的一些語法糖已經(jīng)變得多余。
另外,鏈式語法不能滿足我們想要的編寫代碼的方式。例如:
$('.js-widget') .addClass('is-loading') .show()
這種語法寫起來很簡單,但是根據(jù)我們的標準,它并不能很好地傳達我們的意圖。作者是否期望在當前頁面上有一個或多個 js-widget 元素?另外,如果我們更新頁面標記并意外遺漏了 js-widget 類名,瀏覽器是否會拋出異常會告訴我們出了什么問題?默認情況下,當沒有任何內(nèi)容與選擇器匹配時,jQuery 會跳過整個表達式,但對我們來說,這是一個 bug。
最后,我們開始使用 Flow 來注解類型,以便在構(gòu)建時執(zhí)行靜態(tài)類型檢查,并且我們發(fā)現(xiàn),鏈式語法不適合做靜態(tài)分析,因為幾乎所有 jQuery 方法返回的結(jié)果都是相同的類型。我們當時之所以選擇 Flow,是因為 @flow weak 模式等功能可以讓我們逐步將類型應(yīng)用于無類型的代碼庫上。
總而言之,移除 jQuery 意味著我們可以更多地依賴 Web 標準,讓 MDN Web 文檔成為前端開發(fā)人員事實上的默認文檔,在將來可以維護更具彈性的代碼,并且可以將 30KB 的依賴從我們的捆綁包中移除,加快頁面的加載速度和 JavaScript 的執(zhí)行速度。
雖然定下了最終目標,但我們也知道,分配所有資源一次性移除 jQuery 是不可行的。這種匆匆忙忙的做法可能會導(dǎo)致網(wǎng)站功能出現(xiàn)回歸。相反,我們采取了以下的策略:
1. 設(shè)定指標,跟蹤整一行代碼調(diào)用 jQuery 的比率,并監(jiān)控指標走勢隨時間變化的情況,確保它保持不變或下降,而不是上升。
2. 我們不鼓勵在任何新代碼中導(dǎo)入 jQuery。為了方便自動化,我們創(chuàng)建了 eslint-plugin-jquery(https://github.com/dgraham/eslint-plugin-jquery),如果有人試圖使用 jQuery 功能,例如 $.ajax,CI 檢查將會失敗。
3. 舊代碼中存在大量違反 eslint 規(guī)則的情況,我們在代碼注釋中使用特定的 eslint-disable 規(guī)則進行了注解。看到這些代碼的讀者,他們都該知道,這些代碼不符合我們當前的編碼實踐。
4. 我們創(chuàng)建了一個拉請求機器人,當有人試圖添加新的 eslint-disable 規(guī)則時,會對拉取請求留下評論。這樣我們就可以盡早參與代碼評審,并提出替代方案。
5. 很多舊代碼使用了 pjax 和 facebox 插件,所以我們在保持它們的接口幾乎不變的同時,在內(nèi)部使用 JS 重新實現(xiàn)它們的邏輯。靜態(tài)類型檢查有助于提升我們進行重構(gòu)的信心。
6. 很多舊代碼與 rails-behavior 發(fā)生交互,我們的 Ruby on Rails 適配器幾乎是“不顯眼的”JS,它們將 AJAX 生命周期處理器附加到某些表單上:
// 舊方法 $(document).on('ajaxSuccess', 'form.js-widget', function(event, xhr, settings, data) { // 將響應(yīng)數(shù)據(jù)插入到 DOM 中 })
7. 我們選擇觸發(fā)假的 ajax* 生命周期事件,并保持這些表單像以前一樣異步提交內(nèi)容,而不是立即重寫所有調(diào)用,只是會在內(nèi)部使用 fetch()。
8. 我們自己維護了 jQuery 的一個版本,每當發(fā)現(xiàn)我們不再需要 jQuery 的某個模塊的時候,就會將它從自定義版本中刪除,并發(fā)布更輕量的版本。例如,在移除了 jQuery 的 CSS 偽選擇器之后(如:visible 或:checkbox)我們就可以移除 Sizzle 模塊了,當所有的 $.ajax 調(diào)用都被 fetch() 替換時,就可以移除 AJAX 模塊。
這樣做有兩個目的:加快 JavaScript 執(zhí)行速度,同時確保不會有新代碼試圖使用已移除的功能。
9. 我們根據(jù)網(wǎng)站的分析結(jié)果盡快放棄對舊版本 Internet Explorer 的支持。每當某個 IE 版本的使用率低于某個閾值時,我們就會停止向它提供 JavaScript 支持,并專注支持更現(xiàn)代的瀏覽器。盡早放棄對 IE 8 和 IE 9 的支持對于我們來說意味著可以采用很多原生的瀏覽器功能,否則的話有些功能很難通過 polyfill 來實現(xiàn)。
10. 作為 GitHub.com 前端功能開發(fā)新方法的一部分,我們專注于盡可能多地使用常規(guī) HTML,并且逐步添加 JavaScript 行為作為漸進式增強。因此,那些使用 JS 增強的 Web 表單和其他 UI 元素通常也可以在禁用 JavaScript 的瀏覽器上正常運行。在某些情況下,我們可以完全刪除某些遺留的代碼,而不需要使用 JS 重寫它們。
經(jīng)過多年的努力,我們逐漸減少對 jQuery 的依賴,直到?jīng)]有一行代碼引用它為止。
近年來一直在炒作一項新技術(shù),即自定義元素——瀏覽器原生的組件庫,這意味著用戶無需下載、解析和編譯額外的字節(jié)。
從 2014 年開始,我們已經(jīng)基于 v0 規(guī)范創(chuàng)建了一些自定義元素。然而,由于標準仍然在不斷變化,我們并沒有投入太多精力。直到 2017 年,Web Components v1 規(guī)范發(fā)布,并且 Chrome 和 Safari 實現(xiàn)了這一規(guī)范,我們才開始更廣泛地采用自定義元素。
在移除 jQuery 期間,我們也在尋找用于提取自定義元素的模式。例如,我們將用于顯示模態(tài)對話框的 facebox 轉(zhuǎn)換為 我們的漸進式增強理念也延伸到了自定義元素上。這意味著我們將盡可能多地保留標記內(nèi)容,然后再標記上添加行為。例如, 以下是實現(xiàn) 我們很期待 Web 組件的 Shadow DOM。Shadow DOM 的強大功能為 Web 帶來了很多可能性,但也讓 polyfill 變得更加困難。因為使用 polyfill 會導(dǎo)致性能損失,因此在生產(chǎn)環(huán)境中使用它們是不可行的。 https://githubengineering.com/removing-jquery-from-github-frontend/// local-time 根據(jù)用戶的當前時區(qū)顯示時間。//// 例如://
聯(lián)系客服