写这篇文章的目的,一方面是介绍一下自己编写的模块化 DOM 库 domq.js,另一方面是希望大家对 jQuery 有一个正确的认识,即使 jQuery 已经退出历史舞台,但是它的 API 会以另外一种形式存在下去。
GitHub:https://github.com/nzbin/domq
文档:https://nzbin.gitbooks.io/domq-api/usage.html
jQuery 不会死去从 GitHub 放弃 jQuery,再到 Bootstrap 5 宣布移除 jQuery,看来一个时代终究要落下帷幕。
为什么我们会放弃 jQuery 呢?原因无非这样几个:不需要再进行浏览器的兼容,原生 DOM 查找已经很方便,AJAX 请求有更好的替代方式等等。
在我看来 jQuery 最大的弊端是无法分模块引入,直接引入整个库实在有些不妥,毕竟太多功能已经没有用武之地。但是 jQuery 的 DOM 操作依然很有必要。很多人对我的这个观点有些疑问。其实在使用 MVVM 框架的时候,DOM 操作确实已经很少。但是我们也不可能总是做一些 CRUD 的功能。对于复杂的业务需求仍然需要一些 DOM 操作。
假如 jQuery 可以把 DOM 操作相关的功能模块分离出来,或许还有很大的使用空间。
原生当道在平时的项目中,越来越多的人选择用原生 JS 去操作对象,比如获取元素属性,宽高,定位等等。
早在几年前,github 上就有很多文章介绍如何用原生代替 jQuery,比如 YouDontNeedJQuery,YouMightNotNeedjQuery等。就我个人而言,纯 JS 操作确实很简单,但是并不是很优雅,复杂一点的操作还要经常翻 mdn。
// jquery $('.my #awesome selector'); // js document.querySelectorAll('.my #awesome selector');以上是 jQuery 和原生 js 对比的一个缩影,结果显而易见,jQuery 的 API 更加简洁。除此之外,jQuery 的 API 不仅简洁,而且使用形式也非常统一。相反,原生 JS 的 API 使用方式就比较多样了,比如赋值、传参的形式等。另外原生 JS 的 API 名称冗长,不方便记忆。这也是很多 JS 库诞生的意义。
很多插件一般都会有一个 utils 的文件,基本会对原生方法做一个简单封装并提供一些工具方法。
Zepto 的优势与弱点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 方法。
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 目前基本已经停止维护。正确的方法如下:
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) }) } Domq 的使命形如 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(){ ... } ... }