引入JS文件时document.write的替代方案及动态加载报错问题
你遇到的$未定义报错确实和两种加载方式的执行逻辑直接相关:
document.write插入的脚本会阻塞HTML解析,浏览器必须等脚本下载、执行完毕才会继续解析后续内容,所以后续调用$时jQuery已经加载完成,不会报错。但这种阻塞特性会大幅拖慢页面加载速度,跨域场景下还会被浏览器警告甚至拦截,已经是不推荐使用的写法。createElement + appendChild插入的脚本默认是异步加载的,浏览器会在后台下载脚本的同时继续执行后续代码,所以你调用$的时候jQuery还没有下载执行完成,自然会报错。
针对你的需求(把所有JavaScript引用归集到单个JS文件统一引入),有以下几个可行的替代方案:
方案1:修改依赖代码执行时机,适配异步加载逻辑
你可以把依赖公共脚本的代码放到对应脚本加载完成的回调中执行,修改后的test.js示例如下:
// 动态加载jQuery const jqueryScript = document.createElement('script'); jqueryScript.src = 'https://code.jquery.com/jquery-3.5.1.js'; document.head.appendChild(jqueryScript); // 等jQuery加载完成后再执行依赖代码 jqueryScript.onload = function() { $(function() { console.log("SCRIPT FUNCTION"); }); };
如果需要批量加载多个公共脚本,可以写一个简单的批量加载器,等所有依赖加载完成后再统一执行业务代码:
// 统一管理所有要引入的公共脚本地址 const publicScripts = [ 'https://code.jquery.com/jquery-3.5.1.js', // 其他需要统一引入的工具库、组件库地址 ]; let loadedNum = 0; publicScripts.forEach(src => { const script = document.createElement('script'); script.src = src; script.onload = () => { loadedNum++; // 所有脚本加载完成后执行初始化逻辑 if(loadedNum === publicScripts.length) { initAllPageLogic(); } }; document.head.appendChild(script); }); function initAllPageLogic() { // 这里放所有页面通用的初始化代码 // 如果不同页面有独立业务逻辑,可以约定页面暴露pageInit方法,此处统一调用 if(window.pageInit) window.pageInit(); }
对应HTML页面的业务代码调整为:
function pageInit() { $(function() { console.log("SCRIPT FUNCTION"); }); }
方案2:最小改动兼容老代码,禁用异步加载
如果不想调整原有业务代码的结构,可以给动态创建的script标签添加async = false属性,浏览器会按照脚本插入的顺序同步加载执行,和document.write的加载顺序一致,但不会触发浏览器的跨域拦截警告:
var x = document.createElement('script'); x.src = 'https://code.jquery.com/jquery-3.5.1.js'; x.async = false; // 禁用异步加载,按插入顺序同步执行 document.head.appendChild(x);
添加该属性后,原页面里的$(function(){})代码不需要做任何修改就可以正常运行。
方案3:工程化打包(适合正式项目迭代场景)
如果是正式的商业项目,更推荐用前端打包工具(如Vite、Webpack)把所有公共依赖和业务代码打包成单个/按需拆分的JS文件,直接在HTML里引入打包后的产物即可。这种方式既可以避免动态加载的依赖问题,还能做代码压缩、冗余代码剔除等优化,性能和稳定性都更好。
内容的提问来源于stack exchange,提问作者Ludvig
相关产品推荐
相关产品推荐

