当前位置: 首页 > Web前端 > HTML5

TypeScript中的模块解析过程-ModuleResolution

时间:2023-04-05 02:04:43 HTML5

ModuleResolutionModuleresolution是编译器用来确定import引用的过程。考虑一个导入语句,例如import{a}from"moduleA";.为了检查a的任何使用,编译器需要确切地知道它代表什么,并且需要检查它的定义moduleA。此时,编译器会问“moduleA的形状是什么?”虽然听起来很简单,但可以在您自己的.ts/.tsx文件之一中定义moduleA,或者在.d中定义.ts。首先,编译器试图找到代表导入模块的文件。为此,编译器遵循两种不同策略之一:经典策略或Node.js策略。这些策略告诉编译器去哪里寻找moduleA。如果这不起作用并且模块名称不相关(在“moduleA”的情况下,它是),那么编译器将尝试定位环境模块声明。接下来我们将介绍非相对进口。最后,如果编译器无法解析模块,它将记录一个错误。在这种情况下,错误类似于错误TS2307:找不到模块“moduleA”。相对与非相对模块导入模块导入的解析方式不同,具体取决于模块引用是相对的还是非相对的。相对导入是以/、./或../开头的导入。一些示例包括:从“./components/Entry”导入条目;从“../constants/http”导入{DefaultHeaders};导入“/模组”;任何其他进口被认为是不相关的。一些例子包括:import*as$from"jquery";从“@angular/core”导入{组件};相对导入是相对于导入文件解析的,不能解析为环境模块声明。您应该为您的模块使用相对导入以保证它们在运行时的相对位置。模块解析策略有两种可能的模块解析策略:Node和Classic。您可以使用--moduleResolution标志来指定模块解析策略。如果不指定,--modulecommonjs默认为Node,否则默认为Classic(包括--module设置为amd、system、umd、es2015、esnext等时)。注意:Node模块解析是TypeScript社区中使用最多的,并且推荐用于大多数项目。如果您在解析TypeScript中的导入和导出时遇到问题,请尝试设置moduleResolution:"node"以查看是否可以解决问题。经典解析策略这曾经是TypeScript的默认解析策略。今天,这种策略主要是为了向后兼容。相对导入将相对于导入文件进行解析。因此,从源文件/root/src/folder/A.ts中的“./moduleB”导入{b}将导致以下查找:/root/src/folder/moduleB.ts/root/src/folder/模块B。d.ts但是,对于不相关的模块导入,编译器从包含导入文件的目录开始,沿着目录树向上走,试图找到匹配的定义文件。示例:在源文件/root/src/folder/A.ts中,moduleB的非相对导入,例如import{b}from"moduleB",将导致尝试使用以下命令定位“moduleB”:/root/src/folder/moduleB.ts/root/src/folder/moduleB.d.ts/root/src/moduleB.ts/root/src/moduleB.d.ts/root/moduleB.ts/root/moduleB.d.ts/moduleB.ts/moduleB.d.tsNode模式是一种解析策略,它试图在运行时模仿Node.js模块解析机制。Node.js模块文档中概述了完整的Node.js解析算法。Node.js如何解析模块?要了解TS编译器将遵循哪些步骤,了解Node.js模块很重要。传统上,Node.js中的导入是通过调用一个名为require的函数来执行的。Node.js采取的行为将根据require是相对路径还是非相对路径而有所不同。相对路径相当简单。例如,让我们考虑一个位于/root/src/moduleA.js的文件,其中包含importvarx=require("./moduleB");Node.js按以下顺序解析此导入:询问名为/root/src的文件/moduleB.js的文件是否存在。询问文件夹/root/src/moduleB是否包含名为package.json的文件,指定“主”模块。在我们的示例中,如果Node.js发现文件/root/src/moduleB/package.json包含{"main":"lib/mainModule.js"},那么Node.js将引用/root/src/moduleB/库/主模块。js。询问文件夹/root/src/moduleB是否包含名为index.js的文件。该文件隐含地被认为是文件夹的“主要”模块。但是,非相关模块名称的解析执行方式不同。Node将在名为node_modules的特殊文件夹中查找您的模块。node_modules文件夹可以与当前文件处于同一级别,也可以在目录链中更高。Node将沿着目录链向上走,查看每个node_modules,直到找到您要加载的模块。按照上面的示例,考虑/root/src/moduleA.js是否使用非相对路径并导入varx=require("moduleB");。然后Node将尝试将moduleB解析到每个位置,直到一个正确工作。(1)/root/src/node_modules/moduleB.js(2)/root/src/node_modules/moduleB/package.json(如果指定了“main”属性)(3)/root/src/node_modules/moduleB/index.js(4)/root/node_modules/moduleB.js(5)/root/node_modules/moduleB/package.json(如果它指定了“main”属性)(6)/root/node_modules/moduleB/index.js(7)/node_modules/moduleB.js(8)/node_modules/moduleB/package.json(如果指定了"main"属性)(9)/node_modules/moduleB/index.js注意Node.js在step(4)和(7)跳转一个目录。您可以在Node.js文档中阅读有关从node_modules加载模块的更多信息。TypeScript如何解析模块TypeScript将模仿Node.js运行时解析策略以在编译时定位模块的定义文件。为此,TypeScript覆盖了Node解析逻辑中的TypeScript源文件扩展名(.ts、.tsx和.d.ts)。TypeScript还将在package.json中使用一个名为“types”的字段来反映“main”的用途——编译器将使用它来查找“main”定义文件以进行查阅。例如,/root/src/moduleA.ts中的import{b}from"./moduleB"之类的导入语句将导致以下位置尝试定位“./moduleB”:(1)/root/src/moduleB.ts(2)/root/src/moduleB.tsx(3)/root/src/moduleB.d.ts(4)/root/src/moduleB/package.json(如果它指定了“types”属性)(5)/root/src/moduleB/index.ts(6)/root/src/moduleB/index.tsx(7)/root/src/moduleB/index.d.ts回想一下,Node.js寻找名为moduleB的模块.js文件,然后是适用的package.json,然后是index.js。同样,非相对导入将遵循Node.js解析逻辑,首先查找文件,然后查找适用的文件夹。因此,从源文件/root/src/moduleA.ts中的“moduleB”导入{b}将导致以下查找:/root/src/node_modules/moduleB.ts/root/src/node_modules/moduleB.tsx/root/src/node_modules/moduleB.d.ts/root/src/node_modules/moduleB/package.json(如果它指定了“types”属性)/root/src/node_modules/@types/moduleB.d.ts/root/src/node_modules/moduleB/index.ts/root/src/node_modules/moduleB/index.tsx/root/src/node_modules/moduleB/index.d.ts/root/node_modules/moduleB.ts/root/node_modules/moduleB.tsx/root/node_modules/moduleB.d.ts/root/node_modules/moduleB/package.json(如果它指定了“types”属性)/root/node_modules/@types/moduleB.d.ts/root/node_modules/moduleB/index.ts/root/node_modules/moduleB/index.tsx/root/node_modules/moduleB/index.d.ts/node_modules/moduleB.ts/node_modules/moduleB.tsx/node_modules/moduleB.d.ts/node_modules/moduleB/package.json(如果它指定了“types”属性)/node_modules/@types/moduleB.d.ts/node_modules/moduleB/index.ts/node_modules/moduleB/index.tsx/node_modules/moduleB/index.d.ts不要被这里的步骤数量吓到-TypeScript仍然是这其实并不比什么Node.js复杂。附加模块解析标志项目源布局有时与输出布局不匹配。通常一组构建步骤会产生最终输出。其中包括将.ts文件编译为.js,以及将依赖项从不同的源位置复制到单个输出位置。最终结果是模块在运行时的名称可能与包含其定义的源文件的名称不同。或者最终输出中的模块路径在编译时可能与其对应的源文件路径不匹配。TypeScript编译器有一组额外的标志,用于通知编译器预期在源上发生的转换以生成最终输出。重要的是要注意编译器不执行任何这些转换;它只是使用此信息来指导将模块导入解析到其定义文件中的过程。使用baseUrl是使用AMD模块加载器的应用程序中的常见做法,其中模块在运行时“部署”到单个文件夹。这些模块的源代码可以在不同的目录中,但构建脚本会将它们放在一起。设置baseUrl告诉编译器在哪里可以找到模块。所有具有非相对名称的模块导入都被假定为相对于baseUrl。baseUrl的值由以下因素确定:(1)baseUrl命令行参数的值(如果给定路径是相对路径,则从当前目录计算)(2)'tsconfig.json'中baseUrl属性的值(如果给定的路径是相对的,它是从'tsconfig.json'的位置计算出来的)请注意,相对模块导入不受设置baseUrl的影响,因为它们总是相对于其导入文件进行解析。您可以在RequireJS和SystemJS文档中找到有关baseUrl的更多文档。路径映射有时模块并不直接位于baseUrl下。例如,模块“jquery”的导入将在运行时转换为“node_modules/jquery/dist/jquery.slim.min.js”。加载器使用映射配置在运行时将模块名称映射到文件,请参阅RequireJs文档和SystemJS文档。TypeScript编译器支持使用tsconfig.json文件中的“路径”属性声明此类映射。下面是如何为jquery指定“路径”属性的示例。{"compilerOptions":{"baseUrl":".",//如果是"paths"则必须指定。"paths":{"jquery":["node_modules/jquery/dist/jquery"]//这个映射是相对于"baseUrl"}}}如何查看TypeScript模块解析过程前面提到编译器可以访问文件解析模块时在当前文件夹之外。这可能很难诊断模块未解析或解析为错误定义的原因。使用--traceResolution启用编译器模块解析跟踪可以深入了解模块解析期间发生的情况。假设我们有一个使用typescript模块的示例应用程序。app.ts有一个像import*astsfrom"typescript"这样的导入。│tsconfig.json├────node_modules│└────typescript│└────lib│typescript.d.ts└────srcapp。ts使用如下命令执行编译:tsc--traceResolution结果:========Resolvingmodule'typescript'from'src/app.ts'.========未指定模块解析类型,使用“NodeJs”。从“node_modules”文件夹加载模块“typescript”。文件“src/node_modules/typescript.ts”不存在。文件“src/node_modules”/typescript.tsx'不存在。文件'src/node_modules/typescript.d.ts'不存在。文件'src/node_modules/typescript/package.json'不存在。文件'node_modules/typescript.ts'不存在不存在。文件'node_modules/typescript.tsx'不存在。文件'node_modules/typescript.d.ts'不存在。在'node_modules/typescript/package.json'找到'package.json'。'package.json'有'types'字段'./lib/typescript.d.ts'引用'node_modules/typescript/lib/typescript.d.ts'.File'node_modules/typescript/lib/typescript.d.ts'存在-将其用作模块解析结果。========模块名称'typescript'已成功解析为'node_modules/typescript/lib/typescript.d。TS'。========触发模块解析的源代码位置:========Resolvingmodule'typescript'from'src/app.ts'。========模块解析策略:未指定模块解析种类,使用'NodeJs'。从npm包加载类型:'package.json'有'types'字段'./lib/typescript.d。ts'引用'node_modules/typescript/lib/typescript.d.ts'。最终成功解析的输出:========Modulename'typescript'wassuccessfullyresolvedto'node_modules/typescript/lib/typescript.d.ts'。========