你不需要 jQuery,但你需要一个 DOM 库

花名提莫 2019-03-26 13:23:52 ⋅ 479 阅读

写这篇文章的目的,一方面是介绍一下自己编写的模块化 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 上就有很多文章介绍如何用原生 JS 代替 jQuery,比如 YouDontNeedJQuery,YouMightNotNeedjQuery等。就我个人而言,纯 JS 操作确实很简单,但是并不是很优雅,复杂一点的操作还要经常翻 MDN。

 // 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 是一个思想超前的库,为什么我会有这样的结论?Zepto 对原生方法做了进一步的抽象,使用更简单。正如我在上文说过的,既然 jQuery 的 API 简洁易用,而且我们也更加熟悉,那我们为什么不将 jQuery 和原生 JS 结合起来呢?令人惊讶的是,早在 2010 年,Zepto 的作者就已经这样去做了。用原生 JS 实现了 jQuery 的大部分 API,可替代率接近九成吧,至少在我编写的插件中,几乎可以替换掉所有的 jQuery API。而且 Zepto 也不是一味的使用 document.querySelector 方法,而是根据性能优劣,有选择的使用 document.getElementById 以及 document.querySelector 等。

但是 Zepto 也有一些显而易见的缺陷,毕竟还是上个时代的产物,首先就是无法按需加载,现在我们在写项目的时候更愿意根据自己的需要引入某些方法,而不是将整个库全部引入,虽然 Zepto 的体积不大,但是作为强迫症还是有一些厌恶。另外就是 Zepto 本身也有一些 bug,比如 scrollTopscrollLeft 方法。其它不同参见源码。

 // 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) })
}

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(){
...
}
...
}

当然, 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()
...

安装

npm install domq.js --save

在 domq.js 的 dist 文件夹下有四个主要文件 

dist
├── domq.js (UMD)
├── domq.common.js (CJS)
├── domq.esm.js (ESM)
└── domq.modular.js (MODULAR)

默认加载模块化方案 domq.modular.js

import { D } from 'domq.js'

或者根据需要加载指定文件

import { D } from 'domq.js/dist/domq.esm.js'

模块化使用说明

按需加载需要手动将所需方法挂载到 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 没有太多新的东西,所以也没有太多可以介绍的,它已经在插件 PhotoViewer 以及实际项目中得以运用,欢迎大家下载使用。

总结

这是一个好的时代,也是一个坏的时代,jQuery 的落幕确实让人感叹,但是我们完全没必要因为 jQuery 的落幕而放弃 jQuery 的使用方式。正如前文所说,jQuery 的 DOM 操作在我看来依然是最好用的,所以,你不需要 jQuery,但你需要一个 DOM 库。

---------------END----------------

后续的内容同样精彩

长按关注“IT实战联盟”哦




全部评论: 0

    我有话说:

    可能知道的CRUD

      本系列旨在系统学习提升Mysql技能,更完整内容可以参考阿里新零售数据库设计与实战 DB引擎 可能知道的CRUD INSERT 情况一 Duplicate key 当批量更新,如果

    什么情况下才需要分库分表?

    我请教一下,我模拟测试,搞了几张大表都是3千万左右的数据,带主键查询秒级响应,jmeter插入能够达到每秒6、700条左右,我业务量每秒就是300条左右插入,这个场景下是否需要考虑分库分表的问题?

    想更好的理解Node.js中的Buffer吗?看一下这个。

    不论是否是科班出身,认真读完,想必会给带去一些收获.

    Google技术:了解Google最新发布的JS代码规范 的最佳实践

    Google为了那些还熟悉代码规范的人发布了一个JS代码规范。其中列出了编写简洁易懂的代码所应该做的最佳实践。

    「轻阅读」为什么在做微服务设计的时候需要DDD?

    的设计蓝图里为什么没有看到DDD的影子呢?

    「轻阅读」分布式事务的四种解决方案,成长需要尝试

    分布式事务指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。

    「转载」47 张图带 MySQL 进阶!!!

    我们在  138 张图带 MySQL 入门 中主要介绍了基本的 SQL 命令、数据类型和函数,在具备以上知识后,就可以进行 MySQL 的开发工作了,但是如果要成为一个合格的开发人员

    MySQL数据库开发需要注意的小细节整理

    尽量在数据库做运算控制单表数据量 纯INT超过10M条,含Char超过5M条保持表身段苗条平衡范式和冗

    为什么要使用 Node.js?这几点必须知道!

    经过这几年的发展,前端普遍进入了技术深水区,只会Web页面开发已经难以满足企业的需求,Node逐渐成为了刚性技能。 Node在业务上的使用还没有那么普及,有的时候想用老板还同意,本文将从4个角度

    UCloud 5 元/月云主机,值得拥有

    UCloud 5 元/月云主机,值得拥有

    2018常用JavaScript类

    9个非常有用的Javascript来加速的开发。

    架构实战篇(二)-Spring Boot整合Swagger,让的API可视化

    还在跟前端对接上花费很多的时间而没有效果吗?还在为写接口文档而烦恼吗?今天就教大家一个接口对接神器...

    的老板逼上微服务了吗?

    “ 这些年软件的设计规模越来越庞大,业务需求也越来越复杂,针对系统的性能、高吞吐率、高稳定性、高扩展等特性提出了更高的要求。   图片来自 Pexels可以说业务需求是软件架构能力的

    jQuery 3.6.0 发布,即使存在 JSONP 错误也返回 JSON

    jQuery一个快速、小型且功能丰富的 JavaScript 。通过易于使用的 API(可在多种浏览器中使用),使 HTML 文档的遍历和操作、事件处理、动画和 Ajax 等操作变得更加简单

    VUE 开源收藏版(二):史上最全面的学习资源 ,附GitHub源码地址

    最近做了一个Vue开源项目汇总,里面集合了OpenDigg 上的优质的Vue开源项目,方便移动开发人员便捷的找到自己需要的项目工具等,感兴趣的可以到GitHub上给个star。

    在浏览器输入url到发起http请求,这过程到底发生了什么?

    在浏览器输入url到发起http请求,这过程到底发生了什么?

    强大的 Lambda 式转 Sql 类 - SqlSugar 隐藏功能之 Lambda

    使用场景 1、Lambda to sql 一直是ORM中最难的功能之一,如果有现成的解析那么自已写一个ORM难度将大大降低 2、通过Lambda作为KEY进行缓存操作,特别是仓储模式想要拿到表达式

    「轻阅读」聊一聊6种常用的架构设计模式(上)

      许多现代应用都需要在企业级规模上进行构建,有时甚至需要在互联网规模上进行构建。这些应用都需要满足可扩展性、可用性、安全性、可靠性和弹性需求。 在本文中,我将谈论一些设计模式,这些模式

    精品推荐:如何实现一个TCC分布式事务框架的一点思考

    本文将以Spring容器为例,试图分析一下,实现一个通用的TCC分布式事务框架需要注意的一些问题。