Javascript 为什么从';进行相对导入';父索引文件导致异步模块解析行为?
我有一份回购协议,其结构如下:Javascript 为什么从';进行相对导入';父索引文件导致异步模块解析行为?,javascript,typescript,import,dependencies,Javascript,Typescript,Import,Dependencies,我有一份回购协议,其结构如下: src/ index.ts main.ts bar/ file.ts index.ts foo/ fooFile.ts src/index只是一个顶级索引文件,用于导出包中的所有内容 但是,我正在跟踪的行为实际上需要调用此文件中的某些功能,我稍后将对此进行介绍 src/bar/file.ts导出普通字符串 src/foo/fooFile.ts从父级导入该字符串。索引 // comments relat
src/
index.ts
main.ts
bar/
file.ts
index.ts
foo/
fooFile.ts
src/index
只是一个顶级索引文件,用于导出包中的所有内容
但是,我正在跟踪的行为实际上需要调用此文件中的某些功能,我稍后将对此进行介绍
src/bar/file.ts
导出普通字符串
src/foo/fooFile.ts
从父级导入该字符串。
索引
// comments relate to what happens if you run `node lib/index.js`
import { fileName } from "..";
import {fileName as fileName2} from "../bar";
export const test = "test";
const myData = {
data: fileName, // This resolves to undefined,
data2: fileName2 //This resolves to "bar"
};
export function main() {
console.log(myData);
console.log(fileName); // This resolves to "bar"
}
如果mysrc/index.ts
看起来像:
import {main} from "./foo/fooFile";
export * from "./bar";
export * from "./foo/fooFile";
main();
然后我们得到了这种异步行为—从导入fileName
。
在声明myData
常量时解析为未定义,但在运行时解析字符串
而如果我从。/bar
导入,则在这两个实例中都会得到字符串
即产出:
{ data: undefined, data2: 'bar' }
bar
但是,这种行为似乎只有在我从索引文件调用main()
函数时才会发生。如果我在main.ts中执行相同操作
import {main} from "./index";
main();
我不明白这种行为
{ data: 'bar', data2: 'bar' }
bar
我想,这种行为的原因是节点模块解析和循环依赖性——有人能确切解释为什么会发生这种行为吗
此处的回购协议:
请注意,我已经使用TypeScript创建了此复制-我想这不是问题的原因-但这是我复制实际面临的问题的最佳方式。你是对的,这是因为循环依赖关系 那么在第一种情况下会发生什么呢
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
var fooFile_1 = require("./foo/fooFile");
__exportStar(require("./bar"), exports);
__exportStar(require("./foo/fooFile"), exports);
fooFile_1.main();
索引.ts
index.ts
importsfoo/fooFile.ts
foo/fooFile.ts
foo/fooFile.ts
importsindex.ts
index.ts
已经在处理中,在nodejs中,您将使用export
对象并为其分配属性,然后在require
此文件的某处时,此对象将从require
函数返回。如果存在循环依赖项,则返回“未完成的”导出
对象,因此它获取已分配给它的属性,但nodejs不会尝试继续执行导出该对象的文件,以获取由于循环依赖项而丢失的属性。因此,在本例中,“未完成的”导出
对象只是一个空对象,因为我们没有导出任何内容。这意味着,如果您在foo/fooFile.ts
中从“…”将*作为obj导入,obj
将只是一个空对象。由于导入了{fooFile}
,fooFile
将变得未定义main
函数启动时,index.ts
已经导出了一些变量,包括file
变量,因此main
函数能够使用它
现在是第二个案例。我假设要看到这个问题,我应该删除index.ts
中的main()
行,因为如果我没有-我没有观察到你在说什么,它仍然显示{data:undefined,data2:'bar}
所以我删除了index.ts
中的main()
行。看起来一切都应该是一样的,因为不管怎样,您导入的都是相同的index.ts
文件,如果不是因为一个小问题,事实上也是如此:
请注意,我已经使用TypeScript创建了这个repu-我想这不是问题的原因
实际上有点像。如果编译此项目并在这两种情况下打开index.ts
文件,您将看到这一点。第一种情况:
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
var fooFile_1 = require("./foo/fooFile");
__exportStar(require("./bar"), exports);
__exportStar(require("./foo/fooFile"), exports);
fooFile_1.main();
第二种情况,当我注释掉main()
:
注意到区别了吗?如果删除main()。通常,typescript不会删除它,因为即使导入是无用的,它也可能会产生一些副作用,但在这里,您还是可以稍后从fooFile
导入一些内容:export*from./foo/fooFile'
。嗯,不是导入而是重新导出,但是在nodejs的上下文中,这没有任何区别
所以现在一切都开始有意义了:
开始处理main.ts
,导入index.ts
开始处理index.ts
index.ts
导入/bar
,获取文件名
,然后将其导出。现在这个“未完成的”导出
对象不是空的,它包含{fileName:'bar'}
index.ts
导入/foo/fooFile
/foo/fooFile
导入index.ts
,但它已导出fileName
,因此可以使用它
其余的应该是清楚的
你可以向自己保证,这是实际发生的事情,但改变了进口的顺序。手动在开始时向编译的index.js
文件添加一行require('./foo/fooFile')
,这样可以像第一种情况一样保留导入顺序,并且不会发生任何更改。或者在index.ts
中交换/bar
和/foo/fooFile
导入,这将是相同的
这部分是由typescript造成的,因为如果您只是使用nodejs运行这些文件,它不会删除顶部未使用的导入,并且不会再次发生任何更改