结合开发,带你熟悉package.json与tsconfig.json配置

4,345 阅读18分钟

我们知道,在开发一个项目的时候都需要通过package来管理一个项目,用于描述项目的元信息以及依赖关系。如果你使用ts来开发一个前端项目,那么tsconfig.json文件也是必不可少的。但是在开发中我们又不需要过多的关注这两个配置文件,只需要通过脚手架生成即可。

但是如果你想要发布一个包,那么我们还是需要去了解里面的常见但是不知道啥意思的配置,下面就来带你了解一下这两个配置文件中的一些常见不常用的属性吧。 我们来参考antd组件库讲解。

package.json

下面我们先来看看antd完整的package.json配置

image.png

{
   // 包名
  "name": "antd",
  // 版本号
  "version": "5.24.8",
  // 项目描述
  "description": "An enterprise-class UI design language and React components implementation",
  // 开源协议
  "license": "MIT",
  // 开源项目的资金支持渠道,帮助用户或企业通过捐赠、赞助等方式支持项目维护者
  "funding": {
    "type": "opencollective",
    "url": "https://opencollective.com/ant-design"
  },
  // 项目主页
  "homepage": "https://ant.design",
  // 仓库地址
  "repository": {
    "type": "git",
    "url": "https://github.com/ant-design/ant-design"
  },
  // bug联系地址
  "bugs": {
    "url": "https://github.com/ant-design/ant-design/issues"
  },
  // 项目关键字,mpm包搜索时匹配
  "keywords": [
    "ant",
    "component",
    "components",
    "design",
    "framework",
    "frontend",
    "react",
    "react-component",
    "ui"
  ],
  // 告知打包工具(如 Webpack、Rollup)模块代码是否包含副作用,从而优化 Tree Shaking 的行为。
  "sideEffects": [
    "*.css"  // 所有 CSS 文件有副作用
  ],
  // commonjs模块导入该包的入口
  "main": "lib/index.js", 
  // esmodule模块导入该包的入口
  "module": "es/index.js",
  // 指定包在浏览器中通过 <script> 标签直接加载的 UMD 格式 文件。
  "unpkg": "dist/antd.min.js",
  // 指定 TypeScript 类型声明文件(`.d.ts`)的入口路径
  "typings": "es/index.d.ts",
  // 指定该包上传的npm的文件
  "files": [
    "BUG_VERSIONS.json",
    "dist",
    "es",
    "lib",
    "locale"
  ],
  // 该包运行的脚本
  "scripts": {
    "api-collection": "antd-tools run api-collection",
    "..."
  },
  // 用于指定支持的浏览器
  "browserslist": [
    "defaults"
  ],
  // 对等依赖,无需安装,只需用户项目安装指定的版本即可
  "peerDependencies": {
    "react": ">=16.9.0",
    "react-dom": ">=16.9.0"
  },
  // 该包的依赖
  "dependencies": {
    "@ant-design/colors": "^7.2.0",
    "..."
  },
  // 发布包到 npm 时的特定行为,它可以覆盖 package.json 中的某些默认设置。
  "publishConfig": {
    "registry": "https://registry.npmjs.org/"
  },
  // 检查 JavaScript 包或文件的体积大小,防止意外增加包体积
  "size-limit": [
    {
      "path": "./dist/antd.min.js",
      "limit": "510 KiB",
      "gzip": true
    },
    {
      "path": "./dist/antd-with-locales.min.js",
      "limit": "600 KiB",
      "gzip": true
    }
  ],
  // 非标准字段,某些框架或工具(如静态站点生成器)可能用它表示项目标题。
  "title": "Ant Design",
  // 配置淘宝的 npm 客户端 tnpm(基于 npm 的镜像加速工具),定义私有仓库或镜像源。
  "tnpm": {
    "mode": "npm"
  },
  // 配置 pnpm 包管理器的特定行为(如依赖解析、存储方式)。
  "pnpm": {
    "overrides": {
      "nwsapi": "2.2.20"
    }
  },
  // 强制指定依赖树中某个包的版本(覆盖嵌套依赖),解决依赖冲突问题。
  "overrides": {
    "nwsapi": "2.2.20"
  },
  // 在 Yarn 中用于强制指定依赖版本。
  "resolutions": {
    "nwsapi": "2.2.20"
  }
}

如果你只是开发一个项目,完全不需要关注package.json中的内容,但是如果你想要开发一个第三方包,就需要学习一些配置了。

可以在这里查看具体配置可以根据不同的版本查看不同的配置。

overrides

{
  "overrides": {
    // 全局覆盖 lodash 的版本
    "lodash": "4.17.21",
    
    // 当 react@17.0.0 被安装时,覆盖其依赖的 prop-types 版本
    "react@17.0.0": {
      "prop-types": "15.8.1"
    },
    
    // 覆盖所有父包中依赖的 typescript 版本
    "typescript": "5.0.0"
  }
}

npm命令中强制指定依赖树中某个包的版本(覆盖嵌套依赖),解决依赖冲突问题。它的核心目的是解决依赖冲突、修复安全漏洞或确保依赖版本的一致性。

  • 版本锁定:无论依赖嵌套多深,强制指定某个包的版本。
        {
          "overrides": {
            "baz": {
              "bar": {
                "foo": "1.0.0"
              }
            }
          }
        }
    
  • 修复漏洞:快速升级有安全漏洞的间接依赖。
  • 解决兼容性:确保依赖树中的包版本兼容项目需求。
  • 细粒度控制:针对特定父包的版本,覆盖其子依赖。
    { 
      "overrides": { 
          "bar@2.0.0": { 
              "foo": "1.0.0" 
            } 
          } 
        }
    }
    

如果指定yarn,通过resolutions.overrides配置,pnpm通过pnpm.overrides配置。

files

默认情况下会发布所有位于项目根目录下的文件和文件夹,我们也可以通过.npmignore来配置那些文件被忽略。但是更多的做法是通过配置files属性,这里设置的文件或者文件夹都会被发布,该字段包含的文件不能通过 .npmignore 或 .gitignore 排除。 一般我们只需要发布打包后的文件即可。

"files": [
    "dist",
    "es",
    "lib",
    "locale"
],

但是下方列出的文件始终被发布

  • package.json
  • README
  • LICENSE / LICENCE
  • main字段指定的文件
  • bin 字段对应的文件 (实际测试发现这个也会被默认提交,例如vue-cli中的配置)

下方列出的文件总是被忽略

  • .git
  • CVS
  • .svn
  • .hg
  • .lock-wscript
  • .wafpickle-N
  • .*.swp
  • .DS_Store
  • ._*
  • npm-debug.log
  • .npmrc
  • node_modules
  • config.gypi
  • *.orig
  • package-lock.json (如果您希望发布,请使用 npm-shrinkwrap.json)

peerDependencies

对等依赖,表示当前库无需打包进去,使用开发者安装的包即可。尤其在开发库或框架时非常有用。

"peerDependencies": {
    "react": ">=16.9.0",
    "react-dom": ">=16.9.0"
},

这里的示例说明你的模块需要与 "react","react-dom"16.9.0版本兼容。它告诉使用者,如果他们安装了你的模块,也必须安装并使用特定版本的 "react","react-dom"

peerDependenciesMeta

提供有关对等依赖关系的额外元信息。

"peerDependenciesMeta": {
  "module-name": {
    "optional": true
  }
}

在此示例中,peerDependenciesMeta 允许你提供额外的信息。例如,"optional": true 表示 module-name 是可选的对等依赖。这意味着你的模块可以与 module-name 有更松散的互操作性,而不一定需要强制依赖于特定版本。即使用该包的用户可以不用安装module-name模块。

engines

"engines": {
  "node": ">= 12.0.0",
  "npm": ">= 5.0.0",
  "yarn": "^0.13.0"
}

允许你明确指定你的项目所需要的 Node.js,包安装工具版本范围,确保在安装你的包时使用兼容的 Node.js 版本。使用者在通过npm install下载该包时,如果nodejs版本不符合要求,并不会阻止,它只是提供了一种明确的指示,告诉用户或开发者需要使用特定版本的 Node.js。开发者在安装和使用你的项目时需要自行确保他们使用的 Node.js 版本与 engines 字段中指定的版本兼容。

type

用于指定模块类型的字段。这个字段的作用是告诉 Node.js 或其他工具如何解释项目中的模块。 默认是commonjs

bin

指定脚手架入口文件和脚手架名称。它是命令名称到本地文件名的映射。

{ "bin": { "myapp": "./cli.js" } }

private

如果我们开发的是公司的应用项目,我们需要配置private: true,防止有人不小心把项目发布出去。

module

 "module": "es/index.js",

指定 ES6 模块的入口文件路径。这个对于第三方包开发比较有用吧。他指定的是打包后的esmodule文件路径,当使用者安装该包时,并通过esmodule引入该包,就会查找该字段。

main

 "main": "lib/index.js",

指定该包的入口文件,这个对于第三方包开发比较有用吧。它既可以指定commonjs模块包,也可以指定esmodule模块的包。作用同module。默认是当前工程下的index.js

unpkg

  • 浏览器直接加载:指定包在浏览器中通过 <script> 标签直接加载的 UMD 格式 文件(通常为 .umd.js 或 .min.js)。
  • CDN 服务:当通过 unpkg.com 或 jsdelivr.com 等 CDN 服务引入包时,默认会使用 unpkg 字段指向的文件。

exports

{
  "exports": {
    ".": {
      "import": "./index.js",  // ES模块导入时使用
      "require": "./index.cjs" // CommonJS导入时使用
    },
  }
}

控制外部如何导入你的包。需要Node.js ≥12.16(实验性)或 ≥14.13(稳定)提供了 main 的现代替代方案,允许定义多个入口点,在环境之间支持条件入口解析,并阻止除 “exports” 中定义的入口点之外的任何其他入口点。

  • 定义入口点:指定包的默认导出(主文件)和子路径导出。
  • 条件导出:根据环境(如import/require、Node版本、操作系统等)返回不同的模块。
  • 封装性:仅暴露声明的路径,隐藏内部结构,提升安全性。

imports

{
 // 内部代码可通过`import '#ansi-styles'`或`import '#supports-color/utils'`引用模块,无需冗长的相对路径。
  "imports": {
    "#ansi-styles": "./source/vendor/ansi-styles/index.js", // 别名映射
    "#supports-color": {
        "node": "./source/vendor/supports-color/index.js", // Node环境使用特定路径
        "default": "./source/vendor/supports-color/browser.js" // 其他环境默认路径
    }
  },
}

image.png 控制内部模块的导入映射(类似别名)。需要Node.js ≥14.6

  • 路径别名:简化包内部模块的导入路径(如#ansi-styles指向/source/vendor/ansi-styles/index.js)。
  • 条件导入:根据环境动态解析内部模块(如不同环境加载不同实现)。

browser(?未使用过)

如果你的模块打算在客户端使用,则应使用browser该字段指定入口文件而不是通过main来指定。 这有助于提示用户它可能依赖于 Node.js 模块中不可用的原语。 (例如 window

如果同时配置了 browser 和 main/module,打包工具会优先使用 browser 字段的入口或替换规则。

types

"types": "types/index.d.ts",

指定typescript类型声明文件路径。这告诉 TypeScript 编译器当用户在其项目中导入该包时,应该使用 types/index.d.ts 文件提供的类型信息。

typings

指定 TypeScript 类型声明文件(.d.ts)的入口路径,帮助 TypeScript 用户在使用你的包时获得类型检查和自动补全支持。它是 types 字段的别名,两者功能完全一致,但 types 更常用。确保类型声明文件包含在 files 字段中,否则不会被发布到 npm。

config

"config": {
  "customParam": "some-value"
},
"scripts": {
  // 脚本中直接使用
  "custom-script": "echo $npm_package_config_customParam"
}

用于存储各种配置信息,这些配置信息可以供你的项目中的脚本和命令使用。这些配置选项可以帮助你集中管理项目的设置,使其更具可维护性和可配置性。我们还可以在运行脚本后在文件中通过process.env.npm_package_config_[*]来获取config中配置的属性。

browserslist

"browserslist": [
    "> 0.5%",
    "last 2 versions",
    "Firefox ESR",
    "not dead"
],

用于指定支持的浏览器。

publishConfig

"publishConfig": {
    "registry": "https://registry.npmjs.org/"
},

用于定义在发布 npm 包时的配置选项。这个字段允许你指定发布 npm 包时的一些特定配置,以便更好地控制发布的行为。例如配置包发布地址,而不是发布到npm仓库中。

packageManager

 "packageManager": "npm@10.2.3",

用于指定在安装该项目的依赖包时要使用的包管理器。

directories

声明包中关键目录的路径,帮助开发者和工具理解项目结构。

bin 字段

  • 如果配置了 directories.bin,npm 会将该目录下所有文件视为可执行文件,并自动链接到全局 node_modules/.bin
  • 若同时配置了 bin 字段,优先级为 bin > directories.bin

man 字段

  • 若配置了 directories.man,npm 会将该目录下所有 .数字 文件(如 .1.3)视为手册页。
  • 若同时配置了 man 字段,两者内容会合并。
{
  "directories": {
    "lib": "src",          // 源代码目录
    "bin": "bin",          // 可执行文件目录
    "man": "man",          // 手册页目录
    "doc": "docs",         // 文档目录
    "test": "test",        // 测试目录
    "example": "examples"  // 示例代码目录
  }
}

sideEffects

用于告知打包工具(如 Webpack、Rollup)模块代码是否包含副作用,从而优化 Tree Shaking 的行为。

Tree Shaking 优化

  • 如果模块标记为无副作用("sideEffects": false),打包工具会安全地移除未使用的导出代码
  • 如果模块有副作用,打包工具会保留这些代码,即使它们未被显式使用。

性能提升

  • 减少最终打包体积。
  • 避免误删有副作用的代码(如 CSS 文件、全局 Polyfill)。

workspaces

管理多包仓库(Monorepo) ,允许你在单个根项目中管理多个子包(package)。

  • 集中管理:将多个相关子包聚合在同一个仓库中
  • 自动链接:自动处理子包之间的依赖关系(yarn link/npm link 的自动化替代)
  • 依赖提升:将重复依赖安装到根目录 node_modules,减少磁盘占用
    // 根目录 package.json
    {
      "name": "my-monorepo",
      "private": true,  // 必须设置为私有项目
      "workspaces": [
        "packages/*",    // 匹配 packages 目录下所有子包
        "tools/helper"   // 指定特定子包路径
      ]
    }

git规范的一些配置

如果在项目中需要规范git提交,请查看这里

lint-staged

"lint-staged": {
    "*.{ts,tsx,js,jsx}": "biome format --write",
    "*.{json,less,md}": "prettier --ignore-unknown --write"
},

指定对暂缓区文件执行的脚本命令。

gitHooks

 "gitHooks": {
    "pre-commit": "lint-staged"
  },

指定在git钩子中执行的脚本命令。

所以我们在开发应用项目时,只需要关注lint-staged, gitHooks, type, private, browserslist即可。其他的都是在开发第三方库,需要发布包时关注的。

tsconfig.json

tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项。如果我们指定tsc命令执行编译指定文件,那么它将会忽略tsconfig.json作为编译配置文件。 但是我们可以指定脚手架配置项以及参数,具体看这里

直接使用tsc去编译整个项目中的ts文件,如果在后面指定了特定文件,那么项目中的tsconfig.json将会失效。

tsconfig全配置解析

下面我们通过vben admin后台管理模板中的tsconfig作为例子,分析一些常见的配置。

{
  "compilerOptions": {
    "target": "esnext", // 编译后生成的版本
    "module": "esnext", // 用于指定 TypeScript 编译后生成的模块系统的类型
    "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
    "strict": true, // 严格模式
    "noImplicitThis": false // this指向可以是不确定的
    "forceConsistentCasingInFileNames": true, // 严格区分文件名称大小写
    "allowSyntheticDefaultImports": true, // 模块没有默认导出时,也可以使用import a from b
    "strictFunctionTypes": false, // 函数类型是否严格规范(是否允许传入子类型)
    // preserve:生成代码中会保留JSX以供后续的转换操作使用(比如:Babel).另外,输出文件会带有.jsx扩展名。一般我们会使用babel去处理jsx,不使用tsc去编译。
    "jsx": "preserve", // 指定 JSX 的处理方式。
    "baseUrl": ".", // 表示路径查找前缀,如果是"./src",那么在项目中引入的src目录下的文件只需要写对应的文件名称即可。不需要加上src
    "allowJs": true, // 是否允许编译javascript文件
    "sourceMap": true, // 是否生成sourcemap
    // 开启`esModuleInterop`后会默认开启`allowSyntheticDefaultImports`选项
    "esModuleInterop": true,
    "resolveJsonModule": true, // 是否可以导入json文件解析
    "noUnusedLocals": true, // 是否检查未使用的局部变量。
    "noUnusedParameters": true, // 是否检查未使用的参数。
    "experimentalDecorators": true, // 开启装饰器
    // 指定内置API声明组的列表,
    "lib": ["dom", "esnext"], // 指定要包含在编译中的库文件。就是.d.ts声明文件
    // "指定要包含的类型包名,而不需要在源文件中引用"
    "types": ["vite/client", "jest"],
    // 查找类型定义文件。指定`.d.ts`文件的查找路径。如果指定则只会查找当前指定的目录下的.d.ts文件
    "typeRoots": ["./node_modules/@types/", "./types"],
    "noImplicitAny": false, // 是否禁止隐式 any 类型。
    "skipLibCheck": true, // 是否跳过检查库文件。
    // 配置路径别名,这里配置完毕后还需要在vue.config.ts或者vite.config.ts中的resolve.alias中配置。
    "paths": { 
      "/@/*": ["src/*"],
      "/#/*": ["types/*"]
    }
  },
  // 指定需要编译的文件路径或文件夹路径。
  "include": [
    "tests/**/*.ts",
    "src/**/*.ts",
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "types/**/*.d.ts",
    "types/**/*.ts",
    "build/**/*.ts",
    "build/**/*.d.ts",
    "mock/**/*.ts",
    "vite.config.ts"
  ],
  // 排除需要编译的文件
  "exclude": ["node_modules", "tests/server/**/*.ts", "dist", "**/*.js"]
}

虽然tsconfig配置很多,但是我们开发的时候只需要粘贴上面的配置即可。了解一些项目特有配置配置即可。

paths

项目别名。注意这里配置别名后,我们还需要在对应打包工具的配置文件中配置别名选项。否则不起作用。

"paths": { 
  "/@/*": ["src/*"],
  "/#/*": ["types/*"]
}
// vite.config.ts
resolve: {
  alias: [
    {
      find: 'vue-i18n',
      replacement: 'vue-i18n/dist/vue-i18n.cjs.js',
    },
    // /@/xxxx => src/xxxx
    {
      find: /\/@\//,
      replacement: pathResolve('src') + '/',
    },
    // /#/xxxx => types/xxxx
    {
      find: /\/#\//,
      replacement: pathResolve('types') + '/',
    },
  ],
},

baseUrl

设置文件导入路径前缀。这里设置了,在项目中导入文件就不需要加入前缀了。其实在开发中我们还是设置.表示当前项目根目录,我们只需要配置别名即可。注意这个前缀也是作用于配置别名的路径前缀。

include, exclude

配置编译的文件和排除编译的文件。即配置编译作用范围。

target

将当前ts文件编译成那个版本的js代码。

module

将当前ts文件编译成js文件后使用的模块系统的类型。

moduleResolution

采用哪种方式解析ts中的模块引用。

lib

用于指定 TypeScript 编译器在编译过程中可以使用的 JavaScript 核心库(也称为引用库或内置库)。这些核心库包括了各种 JavaScript 对象、函数和方法的类型定义,以便在 TypeScript 代码中使用这些功能时能够进行类型检查和获得相应的 IntelliSense(代码自动完成和提示)支持。

"lib": ["dom", "esnext"]

这将告诉编译器在编译过程中包含 最新ECMAScript 和浏览器 DOM 的类型定义,以支持在浏览器环境中开发前端应用程序。

types

指定第三方库的类型声明文件,从而提供更好的代码补全和类型检查。而不需要在源文件中引用。

"types": ["vite/client", "jest"]

这个其实可以不设置的,只设置typeRoots即可。

typeRoots

用于指定 TypeScript 编译器查找类型声明(declare)文件根目录。指定.d.ts文件的查找路径。如果指定则只会查找当前指定的目录下的.d.ts文件。

 "typeRoots": ["./node_modules/@types/", "./types"]

我们在项目中使用到一些类型时,不需要再导入类型文件,程序会自动向配置的文件目录查找。对于文件中使用到类型声明文件中得接口,类型别名,还是需要导入后在使用的。

image.png

type和typeRoots的差异

它们都和类型声明文件有关,但是type 字段主要用于指定具体的类型声明文件的名称,而 typeRoots 字段用于指定查找这些类型声明文件的根目录。通常,type 字段用于引入已经存在的类型声明文件,而 typeRoots 字段用于配置类型声明文件的查找路径。

rootDir, outDir

他其实就是和outDir配合使用,将rootDir中的文件编译成js文件输出到outDir指定的目录中。 其实在开发中不会进行配置。而是通过对应的构建工作来决定怎么输出。一般我们还是会使用include进行决定ts编译成js的范围。

例如ts-loader 在内部是调用了 TypeScript 的官方编译器 – tsc。所以,ts-loader 和 tsc 是共享 tsconfig.json

strict

用于设置编译器在编译过程中是否应该执行严格的类型检查。strict 设置为 true 时,TypeScript 编译器会强制执行一系列严格的类型检查,帮助开发者捕捉潜在的错误并提高代码质量。 该属性实际上是一个组合选项,包括了多个严格模式的子选项。

  1. strictNullChecks:当启用时,TypeScript 会对 nullundefined 值进行严格的类型检查,防止在可能为 nullundefined 的值上执行操作,从而减少了潜在的运行时错误。
  2. noImplicitAny:如果设置为 true,TypeScript 将不允许隐式的 any 类型,这意味着所有变量和函数需要显式地声明类型或进行初始化。
  3. strictFunctionTypes:启用时,TypeScript 会对函数类型的兼容性进行更严格的检查,确保函数参数类型是协变的,而函数返回类型是逆变的。
  4. strictPropertyInitialization:当启用时,TypeScript 将要求类中的属性必须在构造函数中进行初始化,以防止在访问未初始化属性时出现错误。
  5. strictBindCallApply:启用时,TypeScript 会对 bindcallapply 方法的参数类型进行严格检查,确保调用这些方法时传入的参数与目标函数的类型兼容。
  6. alwaysStrict:在生成的 JavaScript 代码中始终包含 "use strict" 声明,确保代码始终在严格模式下执行。

declaration

表示编译ts代码时,生成一个.d.ts声明文件。或者使用tsc -d <filepath>生成对应文件的类型声明文件。

这些配置既是开发时常用的,如果后续遇到还会及时更新。

具体的配置还可以查看阮一峰老师的博客

往期文章

专栏文章

最近也在学习nestjs,有一起小伙伴的@我哦。

已辞职,虽然丢了应届生身份,但是看清了一些人的嘴脸,技术还是被很多人认可的。 🎇🎉✨