JavaScript拦截模块导入
JavaScript intercept module import
我有一个使用 SystemJS 的 SPA(在 Aurelia / TypeScript 中,但这应该无关紧要)。假设它在 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
。在这种情况下,我必须拦截 path/location 以将其重定向到 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);
}
这通常不会改变任何东西,但会产生一些控制台输出以查看发生了什么。不幸的是,这似乎确实改变了一些东西,因为我收到错误 Uncaught (in promise) TypeError: this.has is not a function inside system.js
.
然后我尝试用 SystemJS.config({map: ...});
添加映射。令人惊讶的是,这个函数是增量工作的,所以当我调用它时,它不会丢失已经提供的映射。所以我可以这样做:
System.config({map: {
"other-class": `http://otherhost:5002/fetchModule?moduleId=other-class.js`
}});
这不适用于相对路径(那些以 .
或 ..
开头的路径),但是如果我将共享路径放在根目录中 就可以了.
我仍然更愿意拦截加载以便能够处理更多场景,但目前我不知道上述方法中缺少哪个 has
函数。
how can I add mapping dynamically at runtime?
AFAIK 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);
}
}
我不知道这是否适用于打字稿。此外,您还必须弄清楚在您的情况下确切传递给 loader.normalize 的名称。
另外,如果您使用 systemjs builder 来捆绑您的代码,您将需要将该重写添加到 builder 使用的加载器中(这完全是另一回事)。
我有一个使用 SystemJS 的 SPA(在 Aurelia / TypeScript 中,但这应该无关紧要)。假设它在 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
。在这种情况下,我必须拦截 path/location 以将其重定向到 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);
}
这通常不会改变任何东西,但会产生一些控制台输出以查看发生了什么。不幸的是,这似乎确实改变了一些东西,因为我收到错误 Uncaught (in promise) TypeError: this.has is not a function inside system.js
.
然后我尝试用 SystemJS.config({map: ...});
添加映射。令人惊讶的是,这个函数是增量工作的,所以当我调用它时,它不会丢失已经提供的映射。所以我可以这样做:
System.config({map: {
"other-class": `http://otherhost:5002/fetchModule?moduleId=other-class.js`
}});
这不适用于相对路径(那些以 .
或 ..
开头的路径),但是如果我将共享路径放在根目录中 就可以了.
我仍然更愿意拦截加载以便能够处理更多场景,但目前我不知道上述方法中缺少哪个 has
函数。
how can I add mapping dynamically at runtime?
AFAIK 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);
}
}
我不知道这是否适用于打字稿。此外,您还必须弄清楚在您的情况下确切传递给 loader.normalize 的名称。
另外,如果您使用 systemjs builder 来捆绑您的代码,您将需要将该重写添加到 builder 使用的加载器中(这完全是另一回事)。