背景
在很多 Webpack 项目中,我们使用了 webpack alias
,从而支持基于 alias 进行 import:
然而,webpack alias
使用过程中往往会遇到问题:
- import 路径无法进行检查和自动补全(js 通过 eslint 插件可以进行检查,但是依旧无法补全)
- 无法通过「cmd + 点击」进行快速跳转
- 。。。
怎样解决这些问题?正确的配置姿势应该是怎么样的?本文就来讲讲如何正确的配置项目支持短路径引入模块。
统一维护相对路径
首先,需要有一个地方统一维护相对路径的配置,分散到多个地方将不利于维护。
常用的与模块路径相关的配置主要有:
- webpack 配置:主要是配置 alias,webpack 构建时使用
- eslint 配置:主要配置 import/resolver,对 js 的路径进行检查。如果是纯 ts 项目,可以不配置。
- ts 配置:tsconfig.json 中通过 baseUrl 和 paths 指定,主要用于 ts 编译,VSCode 编辑器也会据此对路径进行检查和补全
最合适做法是将相对路径维护在 tsconfg.json 的 baseUrl 和 paths 中,后续其他配置都读取 tsconfig。
关于 tsconfig.json 的 paths 属性
需要注意,paths 只影响 tsc 编译,但不会对输出内容进行修改。也就是说,ts-loader (或 babel-loader) 输出的 js 文件还会是基于相对路径引入的。如果 webpack 不配置对应的 alias,后续构建将会出错。
打通配置
各项配置之间是无关联的的,所以会出现配置了 webpack alias 后无法补全和跳转的情况。我们需要让 webpack 和 eslint 读取 tsconfig 中的配置。
tsconfig.json
首先得有个 tsconfig.json。例如:
{
"compilerOptions": {
"baseUrl": "./src",
// paths基于baseUrl
"paths": {
"@/*": ["./*"],
"mansion/*": ["./pages/mansion/*"],
"something/*": ["./pages/something/*"]
}
// ...other configs
}
}
这样的配置使得 VSCode 和 tsc 能够将'mansion/some/path'
解析到'./src/pages/mansion/some/path'
(基于 tsconfig.json 所处位置),从而进行编译、编辑器路径提示和补全。
值得一提的是,如果 tsconfig 中 allowJS
为 true,VSCode 也会对 js 代码中的相对路径进行解析,从而实现检查补全和跳转。
webpack 配置
此时 webpack 还不知道 tsconfig 的配置,无法据此对相对路径的资源进行引入,打包将会出错。我们可以安装 tsconfig-paths-webpack-plugin
插件,来将 tsconfig 中的 paths 配置同步到 webpack 中。
安装依赖后,在 webpack 配置的 resolve.plugins
(注意不是 plugin)中添加插件:
// webpack/get-webpack-config.js
config.webpackConfig.resolve.plugins.push(
new TsconfigPathsPlugin({
configFile: path.join(workspace, 'tsconfig.json'),
})
);
这样一来,webpack 就能支持 tsconfg 中配置的相对路径,效果和配置 webpack 的 alias 一样。
eslint 配置
目前还有很多项目是 ts 和 js 混合的。在这样的项目中,如果设置了 tsconfig 的 allowJs,编辑器将能够正确解析 js 文件中用到的相对路径,但是 eslint 会有抛出一堆烦人的错误(”import/unresolved’)。一种粗放的做法是将相应规则调成 warning 甚至关掉,但是显然不是我们想要的。
想要让 eslint 识别 tsconfig 中的 paths,可以使用 eslint-import-resolver-typescript
插件。这个插件为 eslint-plugin-import 增加了 ts 和 tsx 文件的支持,更重要的是会读取 tsconfig 来检查 js 中的路径。
使用的话很简单,安装后在.eslintrc.js 中开启即可。
// .eslintrc.js
module.exports = {
extends: '@qunhe/eslint-config-qunhefe/react/index',
settings: {
'import/resolver': {
'eslint-import-resolver-typescript': true,
},
},
};
其他情况
使用 node 或者 ts-node 执行
如果是一个使用 Typescript 写的 nodejs 应用,开发环境往往是直接使用 ts-node 运行 ts 代码,而生产环境使用 node 运行 tsc 编译后的 js 代码。
对于 ts-node 来说,虽然会读取 tsconfig 来编译代码,但是执行的时候非'.'
和'/'
开头的模块会在 node_modules 中进行查找而不是根据 tsconfig 配置来解析,最终执行报错。
node 同理,tsc 编译后的代码依旧包含基于 baseUrl 的相对路径,直接执行将会找不到模块。
此时就需要使用 tsconfig-paths 来将基于 baseUrl 的路径解析到实际的文件上:
## with ts-node
ts-node -r tsconfig-paths/register src/main.ts
## with node
node -r ts-node/register/transpile-only -r tsconfig-paths/register dist/main.js
关于 node 下使用
tsconfig-paths 的文档中写着 node 下的使用和 ts-node 一致,然而实际上有 bug 无法生效,需要额外调用 ts-node/register
使用 babel 转换代码 (不使用 webpack)
- 转换:使用 babel-plugin-module-resolver,不过不支持读取 tsconfig,还需要在 babelrc 中维护一份对应的相对路径的配置用于转换
- lint:可以使用 eslint-import-resolver-typescript(读取 tsconfig),也可以使用 eslint-import-resolver-babel-module(读取 babelrc)