为什么 VSCode 需要 jsconfig.json ?

最近在上手 React 项目的时候,遇到了一些 VSCode 的实践问题。期望的最佳实践是将src 下的模块导入都设置为绝对路径——这样可以无视掉模块之间的关系,强调模块完整的路径。不过,如果所有路径都带上了src 前缀就会很繁琐,所以 VSCode 里有一个类似于tsconfig.jsonjsconfig.json,其中baseUrl可以简化基地址。

一旦设置了jsconfig.json,就会导致部分模块加载异常?

Module Resolution

jsconfig中可以设置编译器选项module,控制模块加载的方式。TypeScript 的模块导入是通过调用Project.resolveModuleNames来执行的。它分为两步,首先是从全局缓存中查找,然后从本地缓存。从函数的调用关系来说是相反的。

TypeScript Server 不同模式的依据是moduletarget选项。在源代码中,classicNameResolver是按照 TypeScript 默认的加载方式,nodeModuleNameResolver则是按照 CommonJS 的方式,也就是 NodeJs 的加载方式。过去默认是前者,现在是后者。

我们知道 TypeScript 通过d.ts(d应该是 definition 的意思)可以增强传统 JavaScript 的类型系统,以便于编译器提供诸如智能提示,导航,重构等高级功能。d.ts从形式上有点类似于 Java 接口的声明。声明相当于静态化了类型,这样编译器就有了可信的类型信息。我没记错的话,以前 TypeScript 刚出来的时候并不支持 JavaScript,但是随后就增加了这样的设计来笼络那些不打算重构,而又希望得到现代编译器好处的库。TypeScript 包含了自动类型获取(Automatic Type Acquisition)的功能,可以将流行的库的定义缓存到全局目录下。在 Mac 上的路径是~/Library/Caches/TypeScript(Windows 路径是%LOCALAPPDATA%\Microsoft\TypeScript)。这就是为什么有时候智能提示这些模块属于这个路径的原因。但是,这些缓存只能被用于 intelliSense,不能用于 auto import。这样做可能也有道理,毕竟如果产生了一个自动导入的 bug,比如将全局 Cache 目录导入了,就会造成很大的问题。

如果不使用相对路径或者绝对路径(这里的绝对路径指的是以 C:\开头),比如直接导入包名,那么本地查询是一个递归的过程,它调用nodeModuleNameResolver来进行解析。这个函数会调用loadModuleFromNearestNodeModulesDirectory来试图获取node_modules目录查询包。这个函数有3步策略:首当其冲的是检查当前包是不是在node_modules下,然后后退一步的策略是loadModuleFromImmediateNodeModulesDirectory,即从当前目录下的node_modules进行查询。如果实在不行就向上查找,然后继续1和2,这里可以参考src/compiler/utilities.ts中的forEachAncestorDirectory方法。

不论使用哪种加载方式,TypeScript 都可以从node_modules下的@types目录获取 dts 的内容。而对于一般的 npm 包而言,可能还没有包含在官方维护的@types中,其 dts 文件随 npm 包一起发布。这种情况下,如果采用 NodeJs 的加载方式,加载是正常的,因为它会从 package.json 里读取types(也可以写作typings,不过已经被废弃了),这个属性标明了包的 dts 文件地址。而使用 ES6 的模块加载方式会无法导入,这就是无法加载模块的原因。解决方法是,如果这个包发布在了@types上,就可以导入这个声明包;否则,需要使用"module": "commonjs"

exclude 排除了node_modules,为什么还能提示?

如果你和我一样有这样的误解,那么不妨再看看前面的Module Resolution 文档。

其他配置可以参考官方文档jsconfig.jsonincludeexclude选项都只是针对source code的。

一些翻看源代码的备注

当我们通过 VSCode 打开项目的时候,对应的是一个Project类。在src/server/editorServices.ts中包含了一个核心类ProjectService,用于创建project类。如果没有配置文件,会调用createInferredProject来创建一个InferredProject类。不过,此时仍然会调用ProjectServicestartWatchingConfigFilesForInferredProjectRoot来监视添加到根目录的文件,所以如果在项目中添加一个jsconfig.json,会触发项目编译环境动态的改变。Project扩展了LanguageServiceHostModuleResolutionHost接口。

编译器内核对应的是program类。

A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions' that represent a compilation unit.

也可以理解为 Standalone Server 和program是对应的。对于 Language Service,为了实现智能提示、重构等功能,它需要支持增量式的构建和文件的监视。

The LanguageServiceHost augments the concept of a file with a version, isOpen flag, and a ScriptSnapshot. Version, allows the language service to track changes to files. isOpen, tells the language service to keep AST in memory as the file is in use. ScriptSnapshot is an abstraction over text that allows the language service to query for changes.

后记

如果一个项目没有显式的使用 require 或者 import 引用库,也没有在package.json里声明这些库的 Types,那么 VSCode 并不会在当前项目中对这个库进行智能提示。此时可以使用typeAcquisition参数来强制将这些库的 Types 包含进来,这样 TypeScript Server 就可以『点』出这些方法啦~

转载于:https://juejin.im/post/5c7ddc13f265da2dd868b4da

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值