JavaScript 拦截模块导入

14

我有一个 SPA(使用 Aurelia/TypeScript 编写,但这并不重要),它使用 SystemJS。假设它在 http://spa:5000/app 上运行。

它有时会从外部 URL(如http://otherhost:5002/fetchmodule?moduleId=waterservice.external.js)按需加载 JavaScript 模块,例如waterservice/external.js。我使用SystemJS.import(url) 来实现,它可以正常工作。

但是,当此外部模块想要导入另一个模块时,使用简单的import { OtherClass } from './other-class'; 时(可以理解为)无法正常工作。在 SPA 加载时,它会查找 http://spa:5000/app/other-class.js。在这种情况下,我必须拦截路径/位置并将其重定向到http://otherhost:5002/fetchmodule?moduleId=other-class.js

注意:对于waterservice/external.ts的 TypeScript 编译是能够正常工作的,因为 TypeScript 编译器可以很容易地找到./other-class.ts。显然,我不能为导入使用绝对 URL。

如何在使用 SystemJS 导入的模块内拦截模块加载?

我已经尝试过的一种方法是在 SystemJS 配置中添加映射。如果像import { OtherClass } from 'other-class'; 这样导入并添加一个映射,例如 "other-class": "http://otherhost:5002/fetchmodule?moduleId=other-class",它可以工作。但如果这种方法是可行的,如何在运行时动态添加映射呢?

其他方法(例如通用的 URL 拦截)也可以。


更新

我尝试按照artem的建议拦截 SystemJS,就像这样:

var systemLoader = SystemJS;
var defaultNormalize = systemLoader.normalize;
systemLoader.normalize = function(name, parentName) {
    console.error("Intercepting", name, parentName);
    return defaultNormalize(name, parentName);
}

通常这不会有任何变化,但会产生一些控制台输出来查看正在发生的情况。不幸的是,似乎确实改变了某些东西,因为我在system.js内收到一个错误Uncaught (in promise) TypeError: this.has is not a function

然后我尝试使用SystemJS.config({map: ...}); 添加映射。令人惊讶的是,该函数是增量的,所以当我调用它时,它不会丢失已提供的映射。因此我可以这样做:

System.config({map: {
   "other-class": `http://otherhost:5002/fetchModule?moduleId=other-class.js`
}});

对于相对路径(以...开头的路径)不起作用,但如果我把共享的文件放在根目录下就可以解决这个问题

虽然我仍然更喜欢拦截加载以处理更多情况,但目前我不知道上述方法中缺少哪个has函数。

1个回答

3
如何在运行时动态添加映射?
据我所知,可以通过调用SystemJS来随时配置它。
SystemJS.config({ map: { additional-mappings-here ... }});

如果这对你不起作用,你可以覆盖loader.normalize并在其中添加自己的模块ID到URL的映射。大致如下:
// assuming you have one global SystemJS instance
var loader = SystemJS;

var defaultNormalize = loader.normalize;
loader.normalize = function(name, parentName) {
    if (parentName == 'your-external-module' && name == 'your-external-submodule') {
        return Promise.resolve('your-submodule-url');
    } else {
        return defaultNormalize.call(loader, name, parentName);
    }
}

我不确定这个方法是否适用于typescript。此外,您需要确定在您的情况下传递给loader.normalize的确切名称。

如果您使用systemjs builder捆绑您的代码,则需要将该覆盖添加到构建器使用的加载程序中(这是一个完全不同的故事)。


因为公共假期的缘故,我现在没有时间查看这个建议,但我会尽快处理。我颁发悬赏是因为它否则会过期。不过我可能会回来问一些问题 ;) - ZoolWay
谢谢您的见解。我已经更新了问题,并提供了两种尝试。您有没有想法为什么在我的情况下拦截normalize会产生错误? - ZoolWay
哦,抱歉我的错误 - defaultNormalize需要this参数: defaultNoramlize.call(loader, name, parentName); - artem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接