☰
jQuery复习指南:核心API、实战案例与原生JS对比
2026/10/16 1:02:54 网站建设 项目流程

好久没碰 jQuery 了,最近因为接手一个维护项目,又把它捡起来用。说实话,第一感觉是“这语法怎么这么啰嗦”,但用了两天之后,反而有点怀念这种直来直去的操作方式。趁着复习完的热乎劲,把这段时间重新上手 jQuery 的要点、踩过的坑、和原生 JS 对比的体会整理成一篇笔记。这篇东西适合两类人看:一类是像我一样平时写 React 或 Vue 写多了、需要临时接老项目的,另一类是刚学前端的同学想搞明白 jQuery 到底在解决什么问题。内容不吹不黑,纯从实用角度出发。

1. 为什么要复习 jQuery:它没有死,只是退到了后台

1.1 存量市场的实际需求仍然庞大

我接触这个维护项目的时候,第一反应也是“这年头还有人在用这个”。但查了一圈发现,市面上仍然有大量已上线的管理系统、CMS 模板、旧版电商后台,它们的核心代码就是 jQuery 写的。有些业务系统每年只改几个字段、调几次接口,完全重构的成本远大于维护成本。这时候,看不懂 jQuery 代码就意味着没法接手这些活。

另一个场景是快速原型。我在做内部工具、活动页、后台辅助脚本的时候,如果项目本身没有工程化环境,不想引入 Node 构建链,直接引入 jQuery 写交互会比手写一堆原生 API 快不少。毕竟它的选择器、链式调用和事件绑定都是封装好的,拿起来就能用。所以在 2024 年这个时间点,我认为复习 jQuery 不是“考古”,而是补一项实务技能。

1.2 jQuery 的设计哲学:一套 API 统一所有浏览器行为

现在写前端可能感受不到,但在 IE 还占半壁江山的年代,每个浏览器对 DOM 的操作差异巨大。jQuery 最核心的贡献,是把 getElementById、querySelectorAll、addEventListener 这些碎片化的操作统一成一套简洁的接口。它的设计逻辑是“选择元素,然后对它做点什么”,这个思维后来也影响了很多现代框架的模板语法。

从复习的角度看,理解这个核心逻辑比记住具体函数重要得多。只要想明白“要操作谁、做什么操作、结果怎么处理”,即使很久不用,翻文档也能立刻恢复手感。我重新上手时就发现,很多方法名都是符合直觉的:addClass 是加类,removeClass 是移除类,toggleClass 是切换类,on 是绑定事件,off 是解绑事件。

引入 jQuery 的时候要注意版本差异。旧项目可能用的是 1.x,它兼容 IE6/7/8 但存在历史遗留 bug;2.x 放弃了一些老 IE 支持;3.x 是现在的新版本,API 整体稳定,但个别行为有变化。接手项目时先确认版本,再决定代码写法,比如 1.x 里常用的$.live、$.die在 1.9 版本以后已经移除,取而代之的是$.on、$.off。

2. 核心 API 的系统复盘:选择器、链式调用和事件委托是主线

2.1 选择器的进阶用法:不只是$("#id")

选择器是 jQuery 最基础也最常用的功能。基础用法很多人会,但有一些进阶技巧在实战中很省事:

// 获取属性包含特定值的元素 $("input[name='username']").val(); // 查找某个元素之后的所有兄弟节点中的第一个 $(".item").nextAll().first(); // 过滤出当前可见的元素 $(".row").filter(":visible"); // 选择空元素(没有子节点和文本节点) $(".wrapper").filter(":empty");

filter和not这两个方法在批量操作时特别有用。比如要移除所有选中项以外的样式,可以用$(".active").not(".current").removeClass("active")。还有个很容易忽略的点:$()返回的是类数组对象,而不是真正的数组。需要转换成数组可以用$.makeArray()或者Array.from(),这样就能调用 map、filter 这些原生的数组方法了。

实际操作中建议先缓存选择结果。如果同一个元素要使用多次,不要每次都写$(".element"),这样会重复遍历整个 DOM。正确做法是:

var $box = $(".box"); $box.css("display", "block"); $box.text("内容已更新");

这既提高性能,也让代码更好读。另外尽量使用 ID 选择器或限定作用域。$("#content .title")比在整棵树里找.title快得多,因为 jQuery 会优先用 ID 定位,再做后代查找。

2.2 链式调用与隐式迭代:jQuery 的优雅所在

链式调用的核心是每个方法返回this对象,所以可以无限接下去。这个设计让代码特别紧凑:

$("#msg") .addClass("alert") .text("请检查必填项") .fadeIn(300) .delay(2000) .fadeOut(300);

但它也有个隐藏问题:链太长时难以调试。如果中间某个方法报错,整条链会中断,很难定位到底哪一步出了问题。我在项目里遇到过一次,链上有个remove()执行后,后续的.css()调用就会报错,因为元素已经被移除了。排查起来很费劲。后来就约定,链式调用最多接四五个方法,超过就拆成多行,并在关键位置加注释。

隐式迭代也是 jQuery 的经典特性。直接对选择器结果集调用方法,jQuery 会循环应用到每个元素上:

$(".row").addClass("highlight"); // 所有 .row 都会加上这个类

这个行为在原生 JS 里要先querySelectorAll再forEach循环,jQuery 把循环封装掉了。但是在需要逐项处理、每个元素不同操作的时候,记得用each:

$(".item").each(function(index, element) { var $el = $(element); var id = $el.data("id"); if (id % 2 === 0) { $el.addClass("even"); } });

each回调里的this指向原生 DOM 元素,不是 jQuery 对象,这是一开始很容易搞错的地方。如果要调用 jQuery 方法,需要重新包装成$(this)。

2.3 事件绑定与委托:动态内容的救命稻草

事件绑定从bind到delegate再到on的演进,是复习时比较重要的一条线。现在统一使用on,它可以实现事件委托。

事件委托解决的问题是:动态添加进来的元素没有绑定事件。比如一个列表,用户点击“添加一行”按钮,新生成了行,但行内的删除按钮点击无效,因为事件在页面初始化时已经绑定完了。解决方式是把事件绑定到父容器上,通过事件冒泡捕获到动态子元素:

// 这种方式绑定到已经存在的 .delete-btn 上 $(".delete-btn").on("click", function() { // 处理点击 }); // 正确姿势:事件绑定在常驻父容器 $("#list").on("click", ".delete-btn", function() { // 这里的 this 指向被点击的 .delete-btn $(this).closest("li").remove(); });

第一次区别的坑我踩过:动态加载的内容一定用委托写法。而且委托层级不要绑得太高,最好绑在直接容器上,比如$("#list")而不是$(document),否则每次点击都得冒泡到 document,性能有损耗,而且会产生意外的联动问题。

肖像移出事件也有讲究。mouseout和mouseleave的区别在于是否冒泡,jQuery 里.mouseleave()更安全,写在父子嵌套结构里不会因为鼠标移到子元素内部就触发事件。这个细节点在开发下拉菜单、悬浮卡片时很关键。

3. 实战复习:做一个列表筛选与排序组件

3.1 业务场景与组件设计思路

光看文档容易忘,非得写一个完整的小组件才能把 API 串起来。我给自己设定了一个场景:有一个商品列表,顶部有几个筛选按钮(全部分类、电子产品、家居用品),还有一个按价格排序的开关。点击按钮,列表就动态更新。

这个组件能同时覆盖选择器、事件委托、数据遍历、DOM 操作、文本插入这几块核心知识。结构上我会把数据源和视图分离,即便用了 jQuery,也保持数据驱动视图的思路,而不是每次都直接改 HTML。

HTML 部分只写一个骨架容器,所有数据通过 JavaScript 渲染:

<div id="app"> <div class="filter-bar"> <button class="filter-btn active">var products = [ { name: "无线鼠标", category: "electronics", price: 59 }, { name: "机械键盘", category: "electronics", price: 299 }, { name: "台灯", category: "home", price: 89 }, { name: "收纳盒", category: "home", price: 29 } ]; function render(list) { var $list = $("#product-list"); $list.empty(); // 清空旧数据 $.each(list, function(index, product) { var $li = $("<li>").addClass("product-item").text(product.name + " - " + product.price + "元"); $li.attr("data-category", product.category); $list.append($li); }); } function getFilteredAndSorted() { var category = $(".filter-btn.active").data("category"); var list = products.filter(function(product) { return category === "all" || product.category === category; }); if (isSortActive) { list.sort(function(a, b) { return a.price - b.price; }); } return list; }

筛选按钮用事件委托统一处理,点击时切换 active 类再重新渲染:

$(".filter-bar").on("click", ".filter-btn", function() { $(".filter-btn").removeClass("active"); $(this).addClass("active"); render(getFilteredAndSorted()); });

排序按钮维护一个布尔变量,每次点击取反,再调用渲染:

var isSortActive = false; $("#sort-btn").on("click", function() { isSortActive = !isSortActive; render(getFilteredAndSorted()); }); render(products);

这里我特意避免直接生成大段 HTML 字符串拼接,而是用$("<li>")创建元素再 append。这样可读性好,也不容易因为引号嵌套出错。如果数据量大,用字符串模板拼好一次性$("#product-list").html(str)会更快,这个可以根据实际场景取舍。

3.3 与现代前端写法的平行对比

写完这个小组件,最大的感受是 jQuery 的写法“命令式”很强,每一步都在声明我要操作哪个 DOM。而 Vue 或 React 的写法是声明式的,只需要维护数据状态,视图自动更新。

用简单类比来说,jQuery 像手动挡汽车,每个操作都要自己换挡,控制感强但多了一步思考;现代框架像自动挡,只管给油(改数据),变速箱自己处理。老项目不一定非得重构,但新项目如果交互复杂、状态多,上框架的效率优势还是明显的。

这个组件里还隐藏着一个痛点:当状态变量多起来之后,手动同步视图会越来越乱。一个按钮改状态,另一个按钮也改状态,最后还要额外维护渲染函数内部的逻辑。jQuery 项目维护成本升高,基本都是从这个点开始的。

4. 实际项目中的高频坑点与排查技巧

4.1 你可能以为选中的元素真的存在

这是 jQuery 新手最容易犯的错误。$(".nonexistent")即使没找到任何元素,也不会返回 null,返回的是一个空集合。这个时候调用.css()、.addClass()都不会报错,但也不会生效,导致问题隐藏得很深。

排查方法是在关键节点打印集合长度:

var $list = $("#list"); console.log($list.length); // 如果为 0,说明选择器没匹配到任何元素

我接手旧项目时遇到过表格插件一个功能失效,之前一直找不到原因。后来加了两行日志才发现是选择器里写了一个不存在的 class,因为页面结构改版把那个 class 去掉了一半,而 jQuery 又不报错,症状就是“控件没反应”而已。

4.2 异步请求与作用域:回调里被改变的 this

jQuery 的 AJAX 默认在回调里把this指向配置对象,不是指向原来的 DOM 元素。所以如果这样写:

$("#btn").on("click", function() { var $btn = $(this); $.get("/api/data", function(res) { $btn.text("加载完成"); // 这里 this 已经不是按钮了 $(this).text("加载完成"); // 错误写法 }); });

正确做法是先把外层this存下来,或者使用箭头函数。在完整项目里,我习惯用$.ajax传完整配置,因为可以直接设置context: this,也可以配合done/fail回调让代码更清晰。这里有个通用原则:凡是异步回调里需要使用“触发事件的元素”,都在进入异步前缓存一份。

4.3 版本选择与 CDN 引用方式

老项目维护里最常遇到的是全局引入方式:window.$。如果项目里同时引入了多个版本的脚本或者混用了别的库,很容易发生$被占用。解决方案是使用jQuery.noConflict():

var jq = $.noConflict(); jq("#button").on("click", function() {});

还有一个排查方向是加载顺序。jQuery 脚本必须在业务代码之前加载,否则业务代码执行时找不到$,控制台会直接报$ is not defined。这种问题一般是模板里脚本顺序被调乱了,或者某个异步加载的组件把 jQuery 延迟了。CDN 建议用一个固定可靠源,锁版本号,不要每天都在变的 “latest” 地址。我一般是锁住大版本,比如3.7.1,既稳定又有安全补丁。

4.4 调试工具与方法

jQuery 的调试,我最依赖的是控制台直接操作。任何一个页面打开控制台输入$(".element"),就能立刻确认选择器有没有匹配到,再输入$(".element").length看数量,输入$(".element").html()看当前内容。这种方式比一遍遍改代码刷新页面快得多。

另外注意$(document).ready的简写。在 jQuery 3.0 之后,$(function(){})在 DOM 加载完成后就会执行,不用等完全加载完。有些开发者会把耗时操作放在 ready 里,感觉页面卡顿。如果不需要操作 DOM 元素的数据初始化,可以直接放在外层,减少等待。

5. 复习之后的判断:什么时候用 jQuery,什么时候别用

5.1 适合用 jQuery 的场景

简单明确,不需要复杂状态同步。比如页面上有少量交互:点击展开收起、表单验证提示、图片懒加载、接口数据回填。这类功能用原生写要不少代码,jQuery 几十行就能搞定,而且老浏览器兼容性有保证。

项目本身没有构建步骤。如果服务器只放静态文件,没有 webpack 或 vite 的流程,引入 jQuery 的额外成本极低,也不需要编译。我见过很多企业内部系统就是这么做的,数据渲染靠后端模板引擎,jQuery 只负责做交互增强,这种架构非常清爽。

5.2 不适合用 jQuery 的场景

组件状态复杂、多模块间需要共享状态。比如购物车数量要同时影响多个位置、操作历史需要回滚,这种场景用 jQuery 手动同步代码会变成一团乱麻。现代框架的数据绑定和状态管理工具应付这类需求要好得多。

对性能要求极高的场景。频繁的大 DOM 更新、动画与长列表渲染,jQuery 操作 DOM 的开销比较大,不如虚拟 DOM 结合 diff 来的效率高。当然,如果列表规模不大,差异其实不明显。

5.3 新旧项目衔接的过渡思路

实际工作里,我见过一种折中的做法:老项目保留 jQuery,但新业务逐步用原生或某框架独立实现,新老通过全局事件通信。这样不会一次性推倒重来,风险可控。小型维护需求直接 jQuery 处理,大型重构或新功能再考虑技术升级。对我这种“好久不用想重新捡起来”的人,核心策略就是:不求精通所有插件,但要把选择器、事件、AJAX、DOM 操作四件事做得熟练,基本能应付大多数维护场景。

复习 jQuery 这件事,不仅是在背 API,更是在换一个角度理解前端开发中“操作 DOM”的本质。那些年我们为兼容不同浏览器做出的努力、形成的封装思想,现在都沉淀在框架的内部实现里。偶尔回看一遍,很多现代框架的概念反而更好理解了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询