You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决WebApp中jQuery动态引用加载不一致的问题?

问题分析与解决方案

你的问题核心是动态脚本加载的异步性导致执行顺序不可控:当前的loadScript函数只是发起脚本请求,不等待加载完成就继续加载下一个,即使在window.onload里调用,多个脚本也是并行加载的。如果ButtonHandlers.js先于VarLoader.js加载完成并执行,自然会找不到readyButton这类在VarLoader里定义的变量。

下面是几种彻底解决问题的方案,按可行性排序:

方案一:改造loadScript为Promise支持,按顺序加载脚本

这是最直接有效的方案,通过Promise保证脚本加载完成后再执行下一个,彻底消除顺序问题。

1. 修改loadScript函数

function loadScript(pathOrUrl) {
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.src = pathOrUrl;
    // 脚本加载成功后触发resolve
    script.onload = () => resolve();
    // 加载失败触发reject,方便排查问题
    script.onerror = (err) => {
      console.error(`加载脚本失败: ${pathOrUrl}`, err);
      reject(err);
    };
    document.head.appendChild(script);
  });
}

2. 用async/await按顺序加载所有脚本

async function loadAllScripts() {
  // 先加载外部依赖,确保jQuery等库先就绪
  await loadScript("https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js");
  await loadScript("https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js");
  await loadScript("https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js");
  await loadScript("https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.5.2/sockjs.min.js");

  await loadScript("/javascripts/gamescripts/ConstLoader.js");

  // 等待DOM完全加载完成(替代原window.onload)
  await new Promise(resolve => window.addEventListener('load', resolve));

  // 按依赖顺序加载游戏脚本,VarLoader必须在所有依赖它的脚本之前
  await loadScript("/javascripts/gamescripts/VarLoader.js");
  await loadScript("/javascripts/gamescripts/SupportFunctions.js");
  await loadScript("/javascripts/gamescripts/SocketLoader.js"); 
  await loadScript("/javascripts/gamescripts/SubscribeFunctions.js"); 
  await loadScript("/javascripts/gamescripts/ButtonHandlers.js"); 
  await loadScript("/javascripts/gamescripts/FrontendHandlers.js"); 
  await loadScript("/javascripts/gamescripts/DnDHandlers.js"); 
  await loadScript("/javascripts/gamescripts/AutoPlaceFunctions.js"); 
  await loadScript("/javascripts/gamescripts/ResetFunctions.js"); 
  await loadScript("/javascripts/gamescripts/RotateFunctions.js"); 
}

// 启动加载流程
loadAllScripts();

这样所有脚本严格按顺序加载执行,VarLoader里的变量一定会在ButtonHandlers之前定义完成。

方案二:优化VarLoader的变量初始化逻辑

如果不想改动加载流程,可以把VarLoader里的变量改成延迟初始化,确保DOM和脚本依赖就绪后再赋值:

修改VarLoader.js

// 先声明变量,不立即赋值
let shipList, ownDivs, enemyDivs, ownBoard, enemyBoard;
let readyButton, resetButton, autoPlaceButton, surrenderButton, rematchButton, backButton;
let dropdownButton, shipDropdown, gameRulesButton, gameRulesDropdown;
let winnerDiv, secondPlayerDiv;
let leftImage, rightImage;

let readyToggle = false;
let rematchToggle = false;
let enemyDivActive = new Array(100).fill(true);

// 新增初始化函数
window.initGameVars = function() {
  // 此时DOM已加载,jQuery也就绪,安全获取元素
  shipList = $(".draggableShip");
  ownDivs = $('.ownDiv');
  enemyDivs = $('.enemyDiv');
  ownBoard = $("#ownBoard");
  enemyBoard = $("#enemyBoard");

  readyButton = $("#readyButton");
  resetButton = $("#resetButton");
  autoPlaceButton = $("#autoPlaceButton");
  surrenderButton = $("#surrenderButton");
  rematchButton = $("#rematchButton");
  backButton = $("#backButton");

  dropdownButton = $("#dropdownButton");
  shipDropdown = $("#dropdownContent");
  gameRulesButton = $("#gamerulesButton");
  gameRulesDropdown = $("#gamerules");

  winnerDiv = $(".winnerDiv");
  secondPlayerDiv = $("#secPlayerD");

  leftImage = $("#leftImage");
  rightImage = $("#rightImage");
};

在依赖脚本开头调用初始化函数

比如在ButtonHandlers.js的最顶部添加:

// 确保变量已初始化
initGameVars();

// 后续的按钮处理逻辑...

这种方案需要修改所有依赖VarLoader变量的脚本,不如方案一彻底,但适合不想大幅改动加载流程的场景。

为什么之前的方法没用?

  • 把VarLoader移到window.onload外:此时DOM可能还没加载完成,jQuery选择器找不到元素,变量会是空的jQuery对象,后续脚本使用时依然会出问题。
  • 延迟加载:网络波动不可控,固定延迟时间要么太短(还是顺序问题)要么太长(影响加载速度),无法保证一致性。
  • try-catch:只能捕获错误,不能解决脚本执行顺序的根本问题。

内容的提问来源于stack exchange,提问作者Tired8296

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:54:53