Skip to content

基于baseUrl和paths引入模块

Published: at 15:27

背景

在很多 Webpack 项目中,我们使用了 webpack alias,从而支持基于 alias 进行 import:

基于alias的import

然而,webpack alias 使用过程中往往会遇到问题:

  1. import 路径无法进行检查和自动补全(js 通过 eslint 插件可以进行检查,但是依旧无法补全)
  2. 无法通过「cmd + 点击」进行快速跳转
  3. 。。。

怎样解决这些问题?正确的配置姿势应该是怎么样的?本文就来讲讲如何正确的配置项目支持短路径引入模块。

统一维护相对路径

首先,需要有一个地方统一维护相对路径的配置,分散到多个地方将不利于维护。

常用的与模块路径相关的配置主要有:

最合适做法是将相对路径维护在 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)