详细梳理浏览器报 Uncaught ReferenceError: $ is not defined 错误的核心原因与排查步骤。涵盖脚本加载顺序、路径错误、CDN失效与协议拦截、作用域冲突及模块化打包等常见场景,提供清晰实用的修复方案。
报错$ is not defined?排查jquery.js未成功加载的常见原因
时间:2026-10-30
浏览:223
文章目录
Uncaught ReferenceError: $ is not defined,本质是当前 JavaScript 执行环境中找不到名为 $ 的变量或函数。对于依赖 jQuery 的项目,这通常意味着 jquery.js 根本没有成功下载、或者自写业务代码在基础库加载就绪之前便抢先执行了。通过本文梳理的“网络链路 → 加载顺序 → 协议限制 → 作用域冲突”四步排查链条,可以快速定位并根治该问题。
一、核心机制:为什么会触发 ReferenceError?
在标准的浏览器环境中,当 jquery.js 脚本被加载并解析时,其源码最外层的立即执行函数会将核心实例挂载到顶层全局对象 window 上,同时建立两个全局属性引用:window.jQuery 以及快捷符号 window.$。
JavaScript 引擎是单线程自上而下解析执行脚本的。当我们在代码中编写诸如 $('#btn') 时,引擎会沿着当前作用域链逐层向上寻找 $ 的声明。若一路查找到 window 顶层仍未命中,引擎就会抛出 Uncaught ReferenceError: $ is not defined 并立刻中断当前脚本的后续执行。
从图 1 可以看出,引发报错的核心分歧点在于代码执行的瞬间,全局对象中是否存在有效定义。即使后端服务器上确实存有 jquery.js 文件,只要它未能在调用发生前完成注入,报错就会必然发生。
二、原因一:脚本引入顺序颠倒或错误使用了异步加载
初学者和快速重构旧页面时最容易发生的错误是引入位置颠倒。例如在 HTML 结构中,先编写了内联操作代码,后才引入 jQuery 脚本库。
1. 典型的顺序颠倒示例
<!-- 错误示例:依赖代码写在了基础库之前 -->
<script>
// 此时浏览器尚未解析到下方的 jquery.js,window.$ 仍为 undefined
$('#submit-btn').on('click', function() {
console.log('提交表单');
});
</script>
<!-- 此时才引入 jQuery,调用已经报错中断 -->
<script src="js/jquery-3.7.1.min.js"></script>
2. 异步属性 async 与 defer 的陷阱
现代网页为了优化首屏渲染性能,经常在 <script> 标签上添加 async 或 defer 属性。这两个属性对执行时序有截然不同的影响:
-
async属性:脚本异步下载,一旦下载完成便立刻执行,完全不保证不同脚本之间的先后执行顺序。如果为jquery.js标记了async,体积较小的业务脚本很可能先于它执行完毕,必然产生$ is not defined。 -
defer属性:脚本异步下载,但会严格按照在 HTML 中声明的先后顺序在DOMContentLoaded事件触发前执行。如果要使用defer,必须确保jquery.js和后续依赖它的业务代码均添加了 defer,且 jQuery 声明在前。
jquery.js 单独添加 async 属性。若普通业务脚本不带 async,两者执行顺序将完全无法预测。
三、原因二:文件路径书写错误导致 404 Not Found
当 jQuery 文件存放在本地服务器时,相对路径在不同路由层级下的解析偏离是导致加载失败的高频诱因。
1. 相对路径与路由层级的脱节
考虑以下目录结构:页面位于 /views/user/profile.html,而脚本位于 /public/js/jquery.min.js:
<!-- 错误写法:在二级目录下使用相对路径 -->
<script src="js/jquery.min.js"></script>
<!-- 浏览器解析出的实际请求为:/views/user/js/jquery.min.js (404 Not Found) -->
<!-- 正确写法:使用站点根目录绝对路径 -->
<script src="/public/js/jquery.min.js"></script>
2. 文件名大小写敏感问题
在本地 macOS 或 Windows 系统开发时,文件系统默认对大小写不敏感,写成 jQuery.min.js 或 jquery.min.js 均可正常访问;然而线上 Linux 服务器默认严格区分大小写,一旦文件名拼写不完全吻合,线上就会立即返回 404 状态码。
JS,刷新页面检查 jquery 请求的状态码。若是红色 404,直接将鼠标悬停在请求条目上,查看实际拼出的 Request URL 并与服务器物理磁盘路径核对。
四、原因三:CDN 节点不可用与 Mixed Content 协议阻断
采用第三方公共 CDN 引入 jQuery 是常见做法,但在生产环境中需要防范网络不可达与浏览器安全策略限制。
1. 混合内容 (Mixed Content) 拦截
当你的站点已经启用 HTTPS 协议(如 https://example.com),而页面中书写的 jQuery 引用依然为 HTTP 明文协议:
<!-- 在 HTTPS 页面中引入 HTTP 资源 -->
<script src="http://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
现代主流浏览器会出于安全策略直接阻断该请求,在控制台抛出 Mixed Content: The page was loaded over HTTPS, but requested an insecure script。此时该请求甚至不会真正发送出去,直接以失败告终。
2. 推荐的 CDN 容灾降级方案 (Local Fallback)
公共 CDN 可能因偶发网络波动、内网环境隔离或服务商维护而暂时不可用。工业级的前端页面推荐使用本地降级回退模式:
<!-- 优先通过 CDN 载入,确保使用 https 或协议相对路径 -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<!-- 容灾检测:若 window.jQuery 不存在,则就地写入本地脚本标签 -->
<script>
window.jQuery || document.write('<script src="/assets/js/jquery-3.7.1.min.js"><\/script>');
</script>
这段代码先检测全局是否存在 window.jQuery。若 CDN 请求失败未完成挂载,则同步向文档流写入本地备用脚本,确保后续代码执行时基础库依然可用。
五、原因四:多库共存导致命名冲突与 noConflict 调用
在部分复杂的遗留系统或企业级门户中,页面可能同时引入了 Prototype.js、MooTools 等同样占用 $ 作为全局标识符的类库,或者其他开发人员在公共脚本中主动调用了 jQuery.noConflict()。
1. noConflict 释放行为
调用 jQuery.noConflict() 会将 window.$ 的控制权交还给最先定义它的外部库,或者恢复为 undefined,但 window.jQuery 仍完整保留。
排查方法:在控制台中分别输入 typeof jQuery 与 typeof $:
- 如果
typeof jQuery === 'function',而typeof $ === 'undefined',即可百分之百断定是别名冲突或被释放。
2. 安全的闭包使用范式
无论外界是否释放了 $,在编写业务代码或组件插件时,均应使用局部作用域来保护别名映射:
// 推荐写法一:在 DOM 准备就绪的回调中直接接收局部参数 $
jQuery(function($) {
// 此作用域内的 $ 严格指向 jQuery,与全局 window.$ 无关
$('#nav-menu').addClass('active');
});
// 推荐写法二:自执行匿名函数 (IIFE)
(function($) {
'use strict';
function initModule() {
$('#sidebar').toggle();
}
window.myModule = { init: initModule };
})(jQuery);
六、原因五:现代工程化模块打包中的作用域隔离
在基于 Webpack、Vite 或 Rollup 构建的现代前端工程中,即便在 package.json 中安装了 jquery 并在入口文件写了 import $ from 'jquery',页面内直接编写的 <script> 标签或第三方旧插件依然可能报 $ is not defined。
1. 模块作用域与全局挂载的区别
ES Module 规范下,import 进来的变量属于模块私有上下文,默认不会自动挂载到 window 全局作用域。如果页面上混用了从 HTML 直接加载的旧插件(例如旧版轮播图或富文本编辑器),这些插件在全局找不到 window.$ 就会报错。
2. 显式挂载全局变量
若必须兼容依赖全局对象的旧脚本,可在项目入口文件(如 main.js)的顶部显式执行全局挂载:
// 入口文件:main.js
import $ from 'jquery';
// 显式将引用挂载至浏览器顶层对象
window.$ = $;
window.jQuery = $;
如果使用 Webpack 进行打包,也可以在 webpack.config.js 中借助内置的 ProvidePlugin 插件自动注入全局映射:
// webpack.config.js
const webpack = require('webpack');
module.exports = {
// ... 其他配置
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
'window.$': 'jquery'
})
]
};
七、系统化排查决策树与速查清单
遇到 $ is not defined 时,不必盲目调整代码,按照以下逻辑决策树进行系统化排查,可在 2 分钟内准确锁定根因:
| 检查层级 | 现象特征 | 核心根因 | 推荐修复措施 |
|---|---|---|---|
| 网络层 | Network 面板出现 404 / 403 / Failed | 文件相对路径写错、大小写不符、CDN 不稳定或 Mixed Content 协议阻断 | 检查 Request URL,改用以 / 开头的绝对路径;协议统一使用 HTTPS;配置 Fallback 本地兜底。 |
| 文档结构层 | Network 面板根本没有 jquery.js 请求 | HTML 遗漏 <script> 标签,或在 SSR / 模板引擎中被条件注释过滤 |
审查 Elements DOM 树,在 <head> 或 <body> 底部显式补全引入标签。 |
| 执行时序层 | 请求 200 成功,但业务代码先执行 | 业务代码位于 jQuery 之前,或为 jQuery 错误添加了 async 属性 |
确保基础库标签物理位置在前;移除 async 属性;统一规范 defer 顺序。 |
| 运行时作用域 | typeof jQuery === 'function' 但 $ === 'undefined' |
调用了 noConflict() 或被其他三方类库占用了 $ |
改用 jQuery(...) 或通过自执行匿名函数 (function($){...})(jQuery) 隔离别名。 |
| 工程构建层 | 构建产物运行正常,但外部插件报错 | ES Module 模块隔离未向 window 暴露全局变量 | 在入口处添加 window.$ = window.jQuery = $; 或配置 Webpack ProvidePlugin。 |
$.fn.jquery,若能准确打印出版本字符串(例如 "3.7.1"),且控制台无任何新抛出的 ReferenceError,则表明 jQuery 已经成功加载并在当前页面中建立起稳定的运行上下文。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。