寫這篇文章的目的,一方面是介紹一下本身編寫的模塊化 DOM 庫 domq.js,另外一方面是但願你們對 jQuery 有一個正確的認識,即便 jQuery 已經逐漸退出歷史舞臺,可是它的 API 將會以另一種形式存在下去。html
GitHub:github.com/nzbin/domqjquery
從 GitHub 放棄 jQuery,再到 Bootstrap 5 宣佈移除 jQuery,看來一個時代終究要落下帷幕。github
爲何咱們會放棄 jQuery 呢?緣由無非這樣幾個:不須要再進行瀏覽器的兼容,原生 DOM 查找已經很方便,AJAX 請求有更好的替代方式等等。api
在我看來 jQuery 最大的弊端是沒法分模塊引入,直接引入整個庫實在有些不妥,畢竟太多功能已經沒有用武之地。可是 jQuery 的 DOM 操做依然頗有必要。不少人對個人這個觀點有些疑問。其實在使用 MVVM 框架的時候,DOM 操做確實已經不多。可是咱們也不可能老是作一些 CRUD 的功能。對於複雜的業務需求仍然須要一些 DOM 操做。瀏覽器
假如 jQuery 能夠把 DOM 操做相關的功能模塊分離出來,或許還有很大的使用空間。框架
在平時的項目中,愈來愈多的人選擇用原生 JS 去操做對象,好比獲取元素屬性,寬高,定位等等。dom
早在幾年前,github 上就有不少文章介紹如何用原生 JS 代替 jQuery,好比 YouDontNeedJQuery,YouMightNotNeedjQuery等。就我我的而言,純 JS 操做確實很簡單,可是並非很優雅,複雜一點的操做還要常常翻 MDN。ide
// jQuery
$('.my #awesome selector');
// JS
document.querySelectorAll('.my #awesome selector');
複製代碼
// jQuery
$(el).hide();
// JS
el.style.display = 'none';
複製代碼
// jQuery
$(el).after(htmlString);
// JS
el.insertAdjacentHTML('afterend', htmlString);
複製代碼
以上是 jQuery 和原生 JS 對比的一個縮影,結果顯而易見,jQuery 的 API 更加簡潔。除此以外,jQuery API 的使用形式也很是統一。相反,原生 JS 的 API 使用方式就比較多樣了,既有賦值,又有傳參等。另外原生 JS 的 API 名稱冗長,不方便記憶。這也是不少 JS 庫誕生的意義。模塊化
不少插件通常都會有一個 utils
的文件,基本會對原生方法作一個簡單封裝並提供一些工具方法。
Zepto 是一個思想超前的庫,爲何我會有這樣的結論?Zepto 對原生方法作了進一步的抽象,使用更簡單。正如我在上文說過的,既然 jQuery 的 API 簡潔易用,並且咱們也更加熟悉,那咱們爲何不將 jQuery 和原生 JS 結合起來呢?使人驚訝的是,早在 2010 年,Zepto 的做者就已經這樣去作了。用原生 JS 實現了 jQuery 的大部分 API,可替代率接近九成吧,至少在我編寫的插件中,幾乎能夠替換掉全部的 jQuery API。並且 Zepto 也不是一味的使用 document.querySelector
方法,而是根據性能優劣,有選擇的使用 document.getElementById
以及 document.querySelector
等。
可是 Zepto 也有一些顯而易見的缺陷,畢竟仍是上個時代的產物,首先就是沒法按需加載,如今咱們在寫項目的時候更願意根據本身的須要引入某些方法,而不是將整個庫所有引入,雖然 Zepto 的體積不大,可是做爲強迫症仍是有一些厭惡。另外就是 Zepto 自己也有一些 bug,好比 scrollTop
、scrollLeft
方法。其它不一樣參見源碼。
// Zepto
scrollTop: function(value) {
if (!this.length) return
var hasScrollTop = 'scrollTop' in this[0]
if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset
return this.each(hasScrollTop ?
function() { this.scrollTop = value } :
function() { this.scrollTo(this.scrollX, value) })
}
複製代碼
document
元素沒法得到正確的值,我對這個問題提過 pr 可是沒有迴應,Zepto 目前基本已經中止維護。正確的方法以下:
// Domq
function scrollTop(value) {
if (!this.length) return
var hasScrollTop = 'scrollTop' in this[0]
if (value === undefined) return hasScrollTop
? this[0].scrollTop
: isWindow(this[0])
? this[0].pageYOffset
: this[0].defaultView.pageYOffset;
return this.each(hasScrollTop ?
function () { this.scrollTop = value } :
function () { this.scrollTo(this.scrollX, value) })
}
複製代碼
形如 jQuery 的 DOM 操做庫有不少,好比 bonzo、$dom,可是在我重構 jQuery 插件時,我發現沒有辦法用這些庫直接替換 jQuery,只有 Zepto 相對完美,可是我又不但願引入額外的無用的方法。
最後我決定改造 Zepto,使之更符合如今的使用習慣。多說一點,我的以爲 Zepto 的核心函數稍顯凌亂,命名空間既有 zepto
、又有 $
、Z
,感受很是混亂,而 domq 的核心函數只有 D
這一個命名空間,形態及功能和 jQuery 的核心函數幾乎同樣,能夠認爲是一個 mini 版的 jQuery。
// Zepto 核心方法
var Zepto = (function() {
var zepto = {};
...
zepto.Z = function(dom, selector) {
return new Z(dom, selector)
}
...
$ = function(selector, context) {
return zepto.init(selector, context)
}
...
})()
複製代碼
// Domq 核心方法
var D = function (selector, context) {
return new D.fn.init(selector, context);
}
D.fn = D.prototype = {
...
init: function(){
...
}
...
}
複製代碼
固然, Domq 最關鍵的仍是按需加載,根據須要掛載方法,儘可能減小沒必要要的代碼。使用方式很簡單,可是你須要建立一個獨立文件,從新掛載須要的方法到 D
命名空間上,這在編寫插件時很是有用。
import {
D,
isArray,
addClass
} from 'domq.js/src/domq.modular';
// 靜態方法
const methods = {
isArray
}
// 原型方法
const fnMethods = {
addClass
}
D.extend(methods);
D.fn.extend(fnMethods);
複製代碼
另外,在作項目時常常會用到一些工具方法,這時候用一個工具庫暴露這些方法或許是最好的方式。Domq 也有一些經常使用的工具方法,不過還須要再迭代一下。
D.type()
D.contains()
D.camelCase()
D.isFunction()
D.isWindow()
D.isEmptyObject()
D.isPlainObject()
D.isNumeric()
D.isArray()
D.inArray()
...
複製代碼
Domq 沒有太多新的東西,因此也沒有太多能夠介紹的,它已經在插件 PhotoViewer 以及實際項目中得以運用,歡迎你們下載使用。
這是一個好的時代,也是一個壞的時代,jQuery 的落幕確實讓人感嘆,可是咱們徹底不必由於 jQuery 的落幕而放棄 jQuery 的使用方式。正如前文所說,jQuery 的 DOM 操做在我看來依然是最好用的,因此,你不須要 jQuery,但你須要一個 DOM 庫。
GitHub:github.com/nzbin/domq