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

如何在前端脚本中按需执行外部源的JS脚本?

问题描述

我正在开发一个从API获取数据的网站,该API以脚本形式提供,通常通过HTML的<script>标签引入:

<script type="text/javascript" src="https://www.fxblue.com/users/dynascalp_demo/overviewscript"></script>

该脚本会创建document.MTIntelligenceAccounts数组并推入所需数据,示例如下:

if (!document.MTIntelligenceAccounts) document.MTIntelligenceAccounts = new Array(); document.MTIntelligenceAccounts.push({ "userid": "dynascalp_demo","balance": 9275.95,"equity": 9275.95,"closedProfit": -724.05,"floatingProfit": 0,"freeMargin": 9275.95,"totalDeposits": 10000,"totalWithdrawals": 0,"totalBankedGrowth": -7.24,"monthlyBankedGrowth": -0.67,"weeklyBankedGrowth": -0.16,"dailyBankedGrowth": -0.03,"bankedProfitFactor": 0.66,"deepestValleyCash": -819.04,"deepestValleyPercent": -8.19,"troughInBalance": 9175.79,"peakInBalance": 10020.11,"historyLengthDays": 331,"averageTradeDurationHours": 2.17,"worstDayPercentage": -1.44,"worstWeekPercentage": -2.32,"worstMonthPercentage": -4.31,"tradesPerDay": 2.5,"totalClosedPositions": 589,"totalOpenPositions": 0,"bankedWinningTrades": 382,"bankedLosingTrades": 207,"bankedBreakEvenTrades": 0,"bankedWinPips": 1486.3,"bankedLossPips": -1604.6,"initialDeposit": 10000,"totalBankedPips":-118.3,"totalOpenPips":0,"peakPercentageLossFromOutset": -8.24,"riskReturnRatio": -1.21,"openAndPendingOrders": []}); 

我的需求是在前端脚本中按需执行该外部脚本:先查询数据库,若数据已存在且未过期(过期指超过1天未更新),则通过Ajax从服务器获取JSON数据;若数据不存在或已过期,则调用该API获取数据并更新数据库。由于后端不支持Node.js,所有逻辑需在前端实现。

目前遇到的问题:

  • 使用Fetch()请求该脚本URL会抛出“Unexpected end of input”错误,请求失败。
  • 尝试动态创建<script>标签的方式,但代码执行后无法正确获取数据并展示,示例代码如下:

示例JS代码

function fxBlue_retrieveAPI() {
document.MTIntelligenceAccounts = new Array();
    const url = "https://www.fxblue.com/users/dynascalp_demo/overviewscript";
//var fxblue_API_Names = ["dynascalp_demo", "fxprogoldrobot", "fxprosilverrobot", "forex_gump_ea"];
var varNames = ["totalDeposits", "balance", "totalBankedGrowth", "monthlyBankedGrowth", "deepestValleyPercent", "historyLengthDays"];
var experts = [];
    var s = document.createElement("script");
    s.setAttribute("type", "text/javascript");
    s.setAttribute("src", url);
    document.body.appendChild(s);
    for (var i = 0; i < document.MTIntelligenceAccounts.length; i++) {
        experts.push({ name: document.MTIntelligenceAccounts[i].userid, id: i });
        if (document.getElementById(experts[i].name + varNames[0])) { document.getElementById(experts[i].name + varNames[0]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].totalDeposits; }
        if (document.getElementById(experts[i].name + varNames[1])) { document.getElementById(experts[i].name + varNames[1]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].balance; }
        if (document.getElementById(experts[i].name + varNames[2])) { document.getElementById(experts[i].name + varNames[2]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].totalBankedGrowth + "%" };
        if (document.getElementById(experts[i].name + varNames[3])) { document.getElementById(experts[i].name + varNames[3]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].monthlyBankedGrowth };
        if (document.getElementById(experts[i].name + varNames[4])) { document.getElementById(experts[i].name + varNames[4]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].deepestValleyPercent + "%" };
        if (document.getElementById(experts[i].name + varNames[5])) { document.getElementById(experts[i].name + varNames[5]).innerHTML = document.MTIntelligenceAccounts[experts[i].id].historyLengthDays };
    }
}

示例HTML代码

<script type="text/javascript" src="/cdn-cgi/scripts/API/jquery-3.1.1.min.js" async></script>
<script type="text/javascript" src="/cdn-cgi/scripts/API/test.js"></script>
<body onload="fxBlue_retrieveAPI()">
    <h3>Total banked growth data example</h3>
    <p id="dynascalp_demototalBankedGrowth"></p>
</body>

请问该如何解决这个问题,实现按需执行外部JS脚本并正确获取数据?


解决方案

1. 修复动态脚本加载的时序问题

你当前代码的核心问题是:动态创建的<script>标签是异步加载的,添加标签后立刻遍历document.MTIntelligenceAccounts时,脚本还未执行完成,数组仍为空。需监听脚本的load事件,确保脚本执行完毕后再处理数据:

function fxBlue_retrieveAPI() {
    document.MTIntelligenceAccounts = [];
    const url = "https://www.fxblue.com/users/dynascalp_demo/overviewscript";
    const varNames = ["totalDeposits", "balance", "totalBankedGrowth", "monthlyBankedGrowth", "deepestValleyPercent", "historyLengthDays"];
    
    const s = document.createElement("script");
    s.type = "text/javascript";
    s.src = url;
    
    // 脚本加载完成后执行数据处理逻辑
    s.onload = function() {
        document.MTIntelligenceAccounts.forEach((account) => {
            const expertName = account.userid;
            varNames.forEach(varName => {
                const elem = document.getElementById(`${expertName}${varName}`);
                if (elem) {
                    let value = account[varName];
                    // 为百分比字段添加%符号
                    if (["totalBankedGrowth", "deepestValleyPercent"].includes(varName)) {
                        value += "%";
                    }
                    elem.textContent = value;
                }
            });
        });
    };
    
    // 处理脚本加载失败的情况
    s.onerror = function() {
        console.error("FXBlue脚本加载失败");
    };
    
    document.body.appendChild(s);
}

2. 实现前端数据缓存逻辑

利用localStorage实现前端缓存,判断数据是否过期,避免重复加载外部脚本:

// 获取缓存数据,判断是否过期
function getCachedFXBlueData() {
    const cached = localStorage.getItem("fxBlueData");
    if (!cached) return null;
    
    const { data, timestamp } = JSON.parse(cached);
    // 1天=86400000毫秒
    if (Date.now() - timestamp > 86400000) {
        localStorage.removeItem("fxBlueData");
        return null;
    }
    return data;
}

// 保存数据到缓存
function saveFXBlueDataToCache(data) {
    localStorage.setItem("fxBlueData", JSON.stringify({
        data,
        timestamp: Date.now()
    }));
}

// 整合缓存逻辑的主函数
function fetchFXBlueData() {
    const cachedData = getCachedFXBlueData();
    if (cachedData) {
        // 使用缓存数据更新页面
        updatePageWithFXBlueData(cachedData);
        return;
    }
    
    // 缓存不存在或过期,加载外部脚本
    document.MTIntelligenceAccounts = [];
    const url = "https://www.fxblue.com/users/dynascalp_demo/overviewscript";
    const s = document.createElement("script");
    s.type = "text/javascript";
    s.src = url;
    
    s.onload = function() {
        const data = document.MTIntelligenceAccounts;
        saveFXBlueDataToCache(data);
        updatePageWithFXBlueData(data);
    };
    
    s.onerror = function() {
        console.error("FXBlue脚本加载失败");
    };
    
    document.body.appendChild(s);
}

// 抽离页面更新逻辑,复用缓存和新数据的更新操作
function updatePageWithFXBlueData(data) {
    const varNames = ["totalDeposits", "balance", "totalBankedGrowth", "monthlyBankedGrowth", "deepestValleyPercent", "historyLengthDays"];
    
    data.forEach((account) => {
        const expertName = account.userid;
        varNames.forEach(varName => {
            const elem = document.getElementById(`${expertName}${varName}`);
            if (elem) {
                let value = account[varName];
                if (["totalBankedGrowth", "deepestValleyPercent"].includes(varName)) {
                    value += "%";
                }
                elem.textContent = value;
            }
        });
    });
}

3. 修改HTML调用逻辑

将页面加载事件改为调用整合后的主函数,同时可补充需要展示的字段元素:

<script type="text/javascript" src="/cdn-cgi/scripts/API/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="/cdn-cgi/scripts/API/test.js"></script>
<body onload="fetchFXBlueData()">
    <h3>Total banked growth data example</h3>
    <p id="dynascalp_demototalBankedGrowth"></p>
    <p id="dynascalp_demobalance"></p>
    <p id="dynascalp_demototalDeposits"></p>
</body>

关键说明

  • 动态脚本必须等待load事件触发后再处理数据,因为脚本加载和执行是异步的。
  • 使用localStorage存储缓存时,注意其5MB左右的容量限制,以及数据时效性的判断逻辑。
  • 务必添加脚本加载失败的错误处理,避免页面无响应或静默报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 02:56:52