1

概述

我正在尝试使用 RequireJS将站点转换为AMD。该站点的大部分内容都依赖于在单独模块中定义的全局变量(主要是实用程序方法)。这给开发人员带来了严重的困难,因为模块并不总是在内容脚本之前加载。

理想的结果

我想要一种方法来防止在加载某些模块之前解析这些页内脚本,以便团队成员可以在时间允许的情况下慢慢删除它们。

研究

  1. 我注意到curl.js可以在同步模式下加载一些脚本。
  2. 我找不到 RequireJS 2+ 的类似内容,尽管我认为它曾经受支持。(我的 git foo 不够强大。)

注意事项

  1. 虽然我知道我可以在开发模式下通过脚本标签加载模块,但我宁愿不这样做,因为这需要在两个地方列出模块。

我试过的

所以我尝试生成一个轮询解决方案,它运行一个粗略定时的 while 循环,然后中断运行一个回调。这是我的实现:

(function (global) { // Making this into a global, so I don't need to wait for the loader's extra timeout. 
    var undef;

    /**
     * Simple args getter.
     *
     * @param {*} arg Used if defined.
     * @param otherwise Used if arg is not defined.
     * @return {*}
     */
    var arg = function (arg, otherwise) {
        return arg !== undef ? arg : otherwise;
    };

    /**
     * Locks up the main thread as soon as the current thread completes.
     *
     * @param {number} time
     */
    var lock = function (time) {
        setTimeout(function () {
            var start = +(new Date),
                current = 0;
            console.log('Locking thread at %d', start);

            start += time;
            while (start > (current = +(new Date))) {
                console.log('tick');
            }

            console.log('Unlocking thread at %d', current);
        }, 0);
    };

    /**
     * Locks up the main thread until a condition is met, releasing it
     * momentarily to allow other "threads" to advance.
     *
     * @param {Function} condition
     * @param {number} [checkInterval=50] How long
     */
    var lockThreadUntil = global.lockThreadUntil = function (condition, checkInterval, releaseDuration) {
        checkInterval = arg(checkInterval, 50);
        releaseDuration = arg(releaseDuration, 0);

        var id = setInterval(function () {
            if (!condition()) {
                lock(checkInterval);
            } else {
                clearInterval(id);
            }
        }, releaseDuration);
    };

    define(function () {
        return lockThreadUntil;
    });
})(this);

在页面上(我测试了一个创建全局的模块):

(function (global) {
    lockThreadUntil(function () {
        var isLoaded = !!global.path && !!global.path.toModule;
        if (!isLoaded) {
            return false;
        }

        console.log('Is loaded!');
        return true;
    }, 1000);
})(this);

在内容脚本中:

console.log('Was needed!');

我希望这会导致 DOM 在给脚本加载时间的同时推进很少,从而允许它在内容脚本之前进入解析堆栈。不幸的是,内容中的脚本似乎首先被触发。

包起来

我愿意接受建议。我正在考虑的选项包括(看起来都很糟糕):

  • 咬牙切齿地使用脚本标签。
  • 吞下 RequireJS 加载方法并用 xhr 实现替换。
4

1 回答 1

0

我建议对您的遗留代码使用 require js shim功能并允许 RequireJS 加载所有内容(包括您的 in-DOM 脚本标签)。Shim 允许您从本质上创建旧版浏览器全局文件的 AMD 版本。shim 配置允许您指定 shim 模块的依赖关系以及它们之间的依赖关系,以及识别它们导出的全局浏览器。

假设您在 DOM 中有旧文件AB作为脚本标签,其中B依赖于文件A (并且脚本标签紧随其后) 。还要说AB都依赖于 AMD 格式的实用程序模块Lib从 DOM 中移除AB的脚本标签,并添加类似于以下内容的 require js 配置块:

requirejs.config({
  shim: {
    'A': ['Lib'],
    'B': ['Lib', 'A']
  }
});

这本质上应该使 RequireJS 在您的 AMD 模块库被解析后加载您的旧脚本。我在这里简化了示例,假设您的所有库 AMD 模块都可以通过单个超级模块Lib来使用。

阅读shim 文档,因为它还解释了如何告诉 require js 你的模块导出什么(就全局对象而言)以使 shim 更加完整。

警告:这仅适用于开发模式。如果您尝试运行 RequireJS 优化器,则结果将无法正常工作,因为标准 AMD 模块上的 shimmed 模块依赖(如此所述)。

此外,此解决方案将要求您在必要时使用 shim 配置中的依赖项手动调整每个 in-DOM 脚本标记的顺序(就像我通过使其依赖于 A 使 B 跟随 A)。在过去,您可以使用 RequireJS订单!插件,但它在 RequireJS 2 中被移除,取而代之的是 shim。这并不是一件坏事,因为它可以帮助您计算出脚本之间的真正依赖关系,并在将来更容易地完全转换到 AMD。

于 2012-08-05T18:00:09.210 回答