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

动态加载Monaco Editor脚本失败,直接加载正常,求排查原因

动态加载Monaco Editor脚本报错问题

我尝试在不阻塞页面加载的情况下按顺序加载JS脚本,编写了一个返回Promise的加载函数,按URL顺序调用后脚本可依次加载,但出现以下错误:

VM4599 monacoManipulate.js:1 Uncaught ReferenceError: monaco is not defined at loader.js:657 GET https://example.com/vs/language/typescript/tsMode.js net::ERR_ABORTED 404

Refused to execute script from 'https://example.com/vs/language/typescript/tsMode.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.

将相同脚本按相同顺序直接在head标签中加载时代码可正常运行,请问动态加载时我哪里出错了?

可正常运行的代码

<head>
  <link rel="stylesheet" data-name="vs/editor/editor.main" href="/monaco-editor/min/vs/editor/editor.main.css" />

  <script defer>
    var require = {
      paths: {
        vs: '/monaco-editor/min/vs'
      }
    };
  </script>

  <script src="/monaco-editor/min/vs/loader.js"></script>
  <script src="/monaco-editor/min/vs/editor/editor.main.nls.js"></script>
  <script src="/monaco-editor/min/vs/editor/editor.main.js"></script>
</head>

无法正常运行的代码

function loadResource(url, callback) {
  return new Promise(function(resolve, reject) {
      var element;
      if (url.endsWith('.js')) {
        element = document.createElement("script");
        element.type = "text/javascript";
        element.src = url;
      } else if (url.endsWith('.css')) {
        element = document.createElement("link");
        element.rel = "stylesheet";
        element.href = url;
        element.type = "text/css";
        if (url === '/monaco-editor/min/vs/editor/editor.main.css') {
          element.dataset.name = "vs/editor/editor.main";
        }
      } else {
        reject(new Error('Unsupported file type ' + url));
        return;
      }

      element.onload = () => {
        if (typeof callback === 'function') {
          resolve(callback());
        } else {
          resolve();
        }
      };
      element.onerror = reject;

      document.getElementsByTagName("head")[0].appendChild(element);
    })
    .catch(function(error) {
      console.error("The resource failed to load:", error);
    });
}

const monaco_css = "/monaco-editor/min/vs/editor/editor.main.css";
const monaco_script = "/monaco-editor/min/vs/loader.js";
const monaco_i18n_script = "/monaco-editor/min/vs/editor/editor.main.nls.js";
const monaco_main_script = "/monaco-editor/min/vs/editor/editor.main.js";
const monaco_manipulate = "/js/users/monacoManipulate.js"

loadResource(monaco_css)
  .then(() => loadResource(monaco_script))
  .then(() => loadResource(monaco_i18n_script))
  .then(() => loadResource(monaco_main_script))
  .then(() => loadResource(monaco_manipulate))
  .then(() => {
    require.config({
      paths: {
        'vs': '/monaco-editor/min/vs'
      }
    });
    require(['vs/editor/editor.main'], function() {
      console.log("Monaco loaded")
    });
  })
  .catch((error) => {
    console.error("Some resources failed to load:", error);
  });

问题分析

  1. require配置时机错误:静态加载时,require配置在loader.js加载前就已全局定义,而动态加载时你把require.config()放在所有脚本加载完成后,导致loader.js初始化时没有正确的路径配置,它会默认从根路径请求vs资源,才会出现https://example.com/vs/...的错误请求(返回404后实际是HTML页面,MIME类型不匹配)。
  2. 操作脚本加载时机错误:monacoManipulate.js依赖monaco全局对象,但你在editor.main.js加载完成后立刻加载它,而Monaco的初始化是异步的,此时monaco还未生成,因此抛出ReferenceError。

修复方案

调整配置时机和脚本加载顺序,和静态加载逻辑对齐:

function loadResource(url, callback) {
  return new Promise(function(resolve, reject) {
      var element;
      if (url.endsWith('.js')) {
        element = document.createElement("script");
        element.type = "text/javascript";
        element.src = url;
      } else if (url.endsWith('.css')) {
        element = document.createElement("link");
        element.rel = "stylesheet";
        element.href = url;
        element.type = "text/css";
        if (url === '/monaco-editor/min/vs/editor/editor.main.css') {
          element.dataset.name = "vs/editor/editor.main";
        }
      } else {
        reject(new Error('Unsupported file type ' + url));
        return;
      }

      element.onload = () => {
        if (typeof callback === 'function') {
          resolve(callback());
        } else {
          resolve();
        }
      };
      element.onerror = reject;

      document.getElementsByTagName("head")[0].appendChild(element);
    })
    .catch(function(error) {
      console.error("The resource failed to load:", error);
    });
}

// 提前全局定义require配置,和静态加载逻辑一致
var require = {
  paths: {
    vs: '/monaco-editor/min/vs'
  }
};

const monaco_css = "/monaco-editor/min/vs/editor/editor.main.css";
const monaco_script = "/monaco-editor/min/vs/loader.js";
const monaco_i18n_script = "/monaco-editor/min/vs/editor/editor.main.nls.js";
const monaco_main_script = "/monaco-editor/min/vs/editor/editor.main.js";
const monaco_manipulate = "/js/users/monacoManipulate.js"

loadResource(monaco_css)
  .then(() => loadResource(monaco_script))
  .then(() => loadResource(monaco_i18n_script))
  .then(() => loadResource(monaco_main_script))
  .then(() => {
    // 直接通过require加载editor.main,无需重复配置
    require(['vs/editor/editor.main'], function() {
      console.log("Monaco loaded");
      // 确认Monaco初始化完成后,再加载依赖它的操作脚本
      loadResource(monaco_manipulate);
    });
  })
  .catch((error) => {
    console.error("Some resources failed to load:", error);
  });

关键说明

  • 静态加载中,defer脚本会在DOM解析完成后执行,但它定义的require全局对象会在loader.js加载前就存在,因此loader.js能正确读取路径配置。
  • 动态加载必须保证require配置在loader.js加载前就全局可用,否则loader.js会使用默认路径,导致资源请求错误。
  • editor.main.js加载完成不代表monaco对象立刻可用,必须通过require(['vs/editor/editor.main'], callback)的回调确认初始化完成,此时再加载依赖脚本才不会报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 07:20:24