首页 > web前端 > js教程 > 失去jQuery膨胀

失去jQuery膨胀

Christopher Nolan
发布: 2025-02-19 08:27:10
原创
275 人浏览过

Lose the jQuery Bloat ­

核心要点

  • NodeList.js 作为 jQuery 的 DOM 操作替代方案,提供类似的功能,但体积更小(压缩后 4k),并利用了原生浏览器 API 的改进。

  • 与 jQuery 不同,NodeList.js 将节点数组视为单个节点,从而实现更简洁的代码和更轻松的 NodeList 对象操作。

  • NodeList.js 包含用于设置和获取属性、调用特定于元素的方法以及访问 NodeList 中节点的特殊方法,以及等同于 jQuery 的 prevObject 属性的 owner 属性。

  • NodeList.js 与特定版本以后的主流浏览器兼容(Firefox 6 、Safari 5.0.5 、Chrome 6 、IE 9 、Opera 11.6 ),并自动更新以包含浏览器新增的方法/属性。

    p.tip { background-color: rgba(128,128,128,0.05); border-top-right-radius: 5px; border-bottom-right-radius: 5px; padding: 15px 20px; border-left: 10px solid rgba(128,128,128,0.075); }

近年来,jQuery 已成为 Web 上事实上的 JavaScript 库。它消除了许多跨浏览器的不一致性,并为客户端脚本添加了一层受欢迎的语法糖。它抽象化处理的主要痛点之一是 DOM 操作,但自 jQuery 问世以来,原生浏览器 API 已得到显著改进,“你可能不需要 jQuery” 的理念开始流行起来。

原因如下:

  1. jQuery 包含许多您不需要或不使用的功能(因此其体积不必要地庞大)。
  2. jQuery 对太多人来说承担了太多功能。通常,较小的库可以更好地完成某些任务。
  3. 就 DOM 操作而言,浏览器 API 现在可以完成 jQuery 大部分的功能。
  4. 浏览器 API 现在更加同步,例如使用 addEventListener 代替 attachEvent

问题何在?

问题在于,与 jQuery 相比,使用原生(或纯)JavaScript 进行 DOM 操作可能很麻烦。这是因为您必须编写更多冗余代码,并处理浏览器无用的 NodeList。

首先,让我们看看 MDN 对 NodeList 的定义:

NodeList 对象是节点的集合,例如 Node.childNodesdocument.querySelectorAll 方法返回的那些节点。

有时还存在动态 NodeList(这可能会令人困惑):

在某些情况下,NodeList 是动态集合,这意味着 DOM 中的更改会反映在集合中。例如,Node.childNodes 是动态的。

这可能是一个问题,因为您无法分辨哪些是动态的,哪些是静态的。除非您从 NodeList 中移除每个节点,然后检查 NodeList 是否为空。如果为空,则表示您拥有一个动态 NodeList(这只是一个坏主意)。

此外,浏览器没有提供任何有用的方法来操作这些 NodeList 对象。

例如,不幸的是,无法使用 forEach 循环遍历节点:

var nodes = document.querySelectorAll('div');
nodes.forEach(function(node) {
  // do something
});
// 错误:nodes.forEach 不是函数
登录后复制
登录后复制
登录后复制
登录后复制

因此,您必须执行以下操作:

var nodes = document.querySelectorAll('div');
for(var i = 0, l = nodes.length; i < l; i++) {
  // do something with nodes[i]
}
登录后复制
登录后复制
登录后复制
登录后复制

或者甚至只能使用“hack”:

[].forEach.call(document.querySelectorAll('div'), function(node) {
    // do something
});
登录后复制
登录后复制
登录后复制
登录后复制

浏览器的原生 NodeList 只有一个方法:item。它通过索引从 NodeList 返回一个节点。当我们可以像使用数组一样访问该节点(使用 array[index])时,它完全没用:

var nodes = document.querySelectorAll('div');
nodes.item(0) === nodes[0]; // true
登录后复制
登录后复制
登录后复制

这就是 NodeList.js 的用武之地——使使用浏览器原生 API 操作 DOM 变得像使用 jQuery 一样容易,但仅需 4k 压缩大小。

解决方案

我创建 NodeList.js 是因为我一直使用原生 DOM API,但希望使它们更简洁,从而减少编写代码时的许多冗余(例如 for 循环)。

NodeList.js 是原生 DOM API 的一个包装器,它允许您操作节点数组(即我的 NodeList),就好像它是一个单个节点一样。这为您提供了比浏览器原生 NodeList 对象更多的功能。

如果您觉得这不错,请从官方 GitHub 存储库获取 NodeList.js 的副本,并继续阅读本教程的其余部分。

用法:

选择 DOM 节点很简单:

$$(selector); // 返回我的 NodeList

此方法在后台使用 querySelectorAll(selector)

但它与 jQuery 相比如何?

很高兴您提出这个问题。让我们将原生 JS、jQuery 和 NodeList.js 进行比较。

假设我们有三个按钮:

让我们将每个按钮的文本更改为“点击我”:

原生 JS:

var buttons = document.querySelectorAll('button'); // 返回浏览器无用的 NodeList
for(var i = 0, l = buttons.length; i < l; i++) {
  buttons[i].textContent = 'Click Me';
}
登录后复制
登录后复制

jQuery:

$('button').text('Click Me');
登录后复制
登录后复制

NodeList.js:

$$('button').textContent = 'Click Me';
登录后复制
登录后复制

在这里,我们看到 NodeList.js 可以有效地将 NodeList 视为单个节点。也就是说,我们引用了一个 NodeList,并将它的 textContent 属性设置为“点击我”。然后,NodeList.js 将对 NodeList 中的每个节点执行此操作。很巧妙,对吧?

如果我们想要方法链(类似于 jQuery),我们会执行以下操作,这将返回对 NodeList 的引用:

$$('button').set('textContent', 'Click Me');
登录后复制
登录后复制

现在,让我们向每个按钮添加一个点击事件监听器:

原生 JS:

var buttons = document.querySelectorAll('button'); // 返回浏览器无用的 NodeList
for(var i = 0, l = buttons.length; i < l; i++) {
  buttons[i].addEventListener('click', function() {
    this.classList.add('clicked');
  });
}
登录后复制

jQuery:

$('button').on('click', function() {
  $(this).addClass('click');
  // 或将 jQuery 与原生混合使用 `classList`:
  this.classList.add('clicked');
});
登录后复制

NodeList.js:

$$('button').addEventListener('click', function() {
  this.classList.add('clicked');
});
登录后复制

好的,所以 jQuery 的 on 方法相当不错。我的库使用浏览器的原生 DOM API(因此是 addEventListener),但这并不会阻止我们为该方法创建别名:

$$.NL.on = $$.NL.addEventListener;

$$('button').on('click', function() {
  this.classList.add('clicked');
});
登录后复制

不错!这演示了我们添加自己方法的方式:

var nodes = document.querySelectorAll('div');
nodes.forEach(function(node) {
  // do something
});
// 错误:nodes.forEach 不是函数
登录后复制
登录后复制
登录后复制
登录后复制

NodeList.js 对数组方法的支持

NodeList.js 确实继承自 Array.prototype,但不是直接继承,因为某些方法已更改,因此使用它们与 NodeList(节点数组)一起使用是有意义的。

pushunshift

例如:pushunshift 方法只能将节点作为参数,否则会抛出错误:

var nodes = document.querySelectorAll('div');
for(var i = 0, l = nodes.length; i < l; i++) {
  // do something with nodes[i]
}
登录后复制
登录后复制
登录后复制
登录后复制

因此,pushunshift 都返回 NodeList 以允许方法链,这意味着它与 JavaScript 的原生 Array#pushArray#unshift 方法不同,后者接受任何内容并返回数组的新长度。如果我们确实想要 NodeList 的长度,我们只需使用 length 属性。

这两个方法,就像 JavaScript 的原生数组方法一样,都会更改 NodeList。

concat

concat 方法将接受以下内容作为参数:

[].forEach.call(document.querySelectorAll('div'), function(node) {
    // do something
});
登录后复制
登录后复制
登录后复制
登录后复制

concat 是一个递归方法,因此这些数组可以像我们希望的那样深,并且会被展平。但是,如果传递的数组中的任何元素不是节点、NodeList 或 HTMLCollection,它将抛出错误。

concat 返回一个新的 NodeList,就像 javascript 的 Array#concat 方法一样。

popshiftmapslicefilter

popshift 方法都可以采用可选参数,说明要从 NodeList 中弹出或移位多少个节点。与 JavaScript 的原生 Array#popArray#shift 不同,后者总是弹出或移位数组中的一个元素,而不管传递什么作为参数。

如果每个映射值都是一个节点,map 方法将返回一个 NodeList;如果不是,则返回映射值的数组。

slicefilter 方法的作用与在真实数组中的作用一样,但会返回一个 NodeList。

由于 NodeList.js 没有直接继承自 Array.prototype,因此如果在加载 NodeList.js 后向 Array.prototype 添加方法,则不会继承该方法。

您可以在此处查看 NodeList.js 的其余数组方法。

特殊方法

NodeList.js 有四个独特的方法,以及一个名为 owner 的属性,它等同于 jQuery 的 prevObject 属性。

getset 方法:

某些元素具有特定于该类型元素的属性(例如,锚标记上的 href 属性)。这就是为什么 $$('a').href 将返回未定义的原因——因为它不是 NodeList 中每个元素都继承的属性。这就是我们如何使用 get 方法访问这些属性的方法:

var nodes = document.querySelectorAll('div');
nodes.forEach(function(node) {
  // do something
});
// 错误:nodes.forEach 不是函数
登录后复制
登录后复制
登录后复制
登录后复制

set 方法可用于为每个元素设置这些属性:

var nodes = document.querySelectorAll('div');
for(var i = 0, l = nodes.length; i < l; i++) {
  // do something with nodes[i]
}
登录后复制
登录后复制
登录后复制
登录后复制

set 还返回 NodeList 以允许方法链。我们可以在 textContent 等方面使用它(两者都等效):

[].forEach.call(document.querySelectorAll('div'), function(node) {
    // do something
});
登录后复制
登录后复制
登录后复制
登录后复制

我们还可以在一次调用中设置多个属性:

var nodes = document.querySelectorAll('div');
nodes.item(0) === nodes[0]; // true
登录后复制
登录后复制
登录后复制

以上所有操作都可以使用任意属性完成,例如 style

var buttons = document.querySelectorAll('button'); // 返回浏览器无用的 NodeList
for(var i = 0, l = buttons.length; i < l; i++) {
  buttons[i].textContent = 'Click Me';
}
登录后复制
登录后复制

call 方法

call 方法允许您调用特定于元素的方法(例如,视频元素上的 pause):

$('button').text('Click Me');
登录后复制
登录后复制

item 方法

item 方法等同于 jQuery 的 eq 方法。它返回一个 NodeList,其中只包含传递索引的节点:

$$('button').textContent = 'Click Me';
登录后复制
登录后复制

owner 属性

owner 属性等同于 jQuery 的 prevObject

$$('button').set('textContent', 'Click Me');
登录后复制
登录后复制

btns.style 返回样式数组,而 owner 将返回 style 所映射的 NodeList。

NodeList.js 兼容性

我的库与所有主要的新浏览器兼容,如下所述。

浏览器版本FireFox6 Safari5.0.5 Chrome6 IE9 Opera11.6

结论

现在我们终于可以使用有用的 NodeList 对象了!

对于大约 4k 的压缩大小,您可以获得上述所有功能以及更多功能,您可以在 NodeList.js 的 GitHub 存储库中了解所有这些功能。

由于 NodeList.js 使用浏览器作为依赖项,因此无需进行任何升级。每当浏览器向 DOM 元素添加新方法/属性时,您都可以通过 NodeList.js 自动使用这些方法/属性。所有这一切都意味着您唯一需要担心的弃用是浏览器删除的方法。这些通常是使用率非常低的方法,因为我们不能破坏 Web。

那么您怎么看?您会考虑使用这个库吗?是否缺少任何重要功能?我很乐意在下面的评论中听到您的意见。

关于使用 NodeList.js 进行 DOM 操作的常见问题

NodeList 和 HTMLCollection 有什么区别?

NodeList 和 HTMLCollection 都是节点集合。它们之间的主要区别在于 NodeList 可以包含任何节点类型,而 HTMLCollection 是元素节点的集合。HTMLCollection 也是动态的,这意味着当文档结构发生更改时,它会自动更新。另一方面,NodeList 是静态的,不会更新以反映文档中的更改。

如何将 NodeList 转换为数组?

您可以使用 Array.from() 方法或展开运算符将 NodeList 转换为数组。以下是如何操作:

var nodes = document.querySelectorAll('div');
nodes.forEach(function(node) {
  // do something
});
// 错误:nodes.forEach 不是函数
登录后复制
登录后复制
登录后复制
登录后复制

为什么 jQuery 选择器返回 prevObject 而不是普通元素?

jQuery 的链式机制通过在进行更改之前存储之前的对象来工作。这允许您使用 .end() 方法恢复到之前的状态。如果您想获取实际的 DOM 元素,可以使用 .get() 方法或数组表示法。

如何循环遍历 NodeList?

您可以使用 for 循环、for...of 循环或 forEach() 方法循环遍历 NodeList。这是一个使用 for 循环的示例:

var nodes = document.querySelectorAll('div');
for(var i = 0, l = nodes.length; i < l; i++) {
  // do something with nodes[i]
}
登录后复制
登录后复制
登录后复制
登录后复制

jQuery 中 .prev() 方法有什么用?

jQuery 中的 .prev() 方法用于选择所选元素的紧邻前一个同级元素。如果提供了选择器,则只有在匹配该选择器时才会检索前一个同级元素。

jQuery 在 2022 年仍然相关吗?

虽然 jQuery 在发布时是一个改变游戏规则的东西,但现代 JavaScript 生态系统已经发生了显着变化。许多使 jQuery 受欢迎的功能现在都内置于 JavaScript 本身。但是,jQuery 仍然被广泛使用和维护,它可能是某些项目的良好选择。

如何从 NodeList 中选择特定节点?

您可以使用数组表示法或 item() 方法从 NodeList 中选择特定节点。以下是如何操作:

[].forEach.call(document.querySelectorAll('div'), function(node) {
    // do something
});
登录后复制
登录后复制
登录后复制
登录后复制

我可以在 NodeList 上使用 map、filter 和 reduce 方法吗?

NodeList 不是数组,因此它没有像 map、filter 和 reduce 这样的方法。但是,您可以将 NodeList 转换为数组,然后使用这些方法。

querySelector 和 querySelectorAll 有什么区别?

querySelector 返回文档中与指定的 CSS 选择器匹配的第一个元素,而 querySelectorAll 返回与 CSS 选择器匹配的所有元素的 NodeList。

如何检查 NodeList 是否为空?

您可以通过检查其 length 属性来检查 NodeList 是否为空。如果长度为 0,则 NodeList 为空。以下是如何操作:

var nodes = document.querySelectorAll('div');
nodes.item(0) === nodes[0]; // true
登录后复制
登录后复制
登录后复制

以上是失去jQuery膨胀的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板