npm
是与 Node.js
一起安装的包管理工具,它能解决 Node.js 代码部署中的许多问题,常见的使用场景包括:
- 允许用户从
NPM
服务器下载别人编写的第三方包到本地使用; - 允许用户从
NPM
服务器下载并安装别人编写的命令行程序到本地使用; - 允许用户将自己编写的包或命令行程序上传到
NPM
服务器供别人使用。
在前端开发中,npm
已经成为不可或缺的工具,它提供了依赖安装、卸载、升级和发布等一条龙服务,大大提升了开发效率。
npm
制定了包的标准规范,包的根目录必须包含 package.json
文件,该文件定义了包的所有信息,如包名、版本号、依赖包等。Node.js 内置的 require()
方法会默认查找 node_modules
目录中的依赖,而无需手动指定路径,这一机制让包的开发、依赖安装、发布等过程更加规范化、自动化。
接下来,让我们深入了解 npm
包的配置文件 package.json
。
包与模块的区别
包 是一个包含 package.json
文件的目录,它可以在 npm
上发布,并供其他开发者使用。包通常包含多个模块,并通过 npm
进行版本控制和发布。
模块 是 node_modules
目录下的任何可以被 require()
或 import
加载的文件或文件夹。模块可以是单一的 JavaScript 文件,也可以是包含多个文件的目录。
简而言之,包 是一个包含多个模块的集合,而 模块 是包中的单个功能单元。
举个例子,moment
包就是由多个模块组成的,其中 moment.js
是一个主要模块。其 package.json
文件的内容可能如下:
// `moment` 包的 package.json
{
"name": "moment",
"version": "2.29.1",
"main": "moment.js"
}
在这个例子中,通过 const moment = require('moment')
或 import moment from 'moment'
,可以加载 moment.js
作为模块使用。require()
或 import
会根据 package.json
中的 main
字段指定的入口文件(即 moment.js
)来加载模块。
package.json
字段详解
1. name
(名称)
name
字段定义了包的名称,且在发布时非常重要。命名规则如下:
- 必须小于或等于 214 个字符;
- 不能以
.
、_
、大写字母或中文开头; - 不能与 Node.js 核心模块名称相同;
- 包名可以包含字母、数字、破折号和下划线等 URL 安全字符。
如果不打算发布包,name
字段可以省略。
2. version
(版本)
version
字段定义了包的版本号,遵循语义化版本控制规范(X.Y.Z
):
X
主版本号:涉及重大功能更新或破坏性变更时更新;Y
次版本号:新增功能但向下兼容时更新;Z
修订号:修复问题且未产生破坏性变更时更新。
版本号可以包括预发布版本,如 1.0.0-beta
。
3. description
(描述)
description
字段提供了包的简短描述,帮助开发者在 npm
中快速了解包的功能。例如,React
包的描述为:
"description": "A JavaScript library for building user interfaces"
4. keywords
(关键字)
keywords
字段是一个数组,包含了与包相关的搜索关键字,有助于提高在 npm
搜索中的曝光率。例如,axios
的关键字如下:
"keywords": ["xhr", "http", "ajax", "promise", "node"]
5. homepage
(主页)
homepage
字段指向项目的主页,通常是 GitHub 链接或官方文档主页:
"homepage": "https://github.com/facebook/react"
6. bugs
(问题追踪)
bugs
字段指定问题报告的链接,通常指向 GitHub 的 issue 页面:
"bugs": "https://github.com/facebook/react/issues"
7. license
(许可证)
license
字段指明项目的开源许可证,常见的有 MIT
、ISC
等:
"license": "MIT"
8. repository
(仓库)
repository
字段指定代码存储的位置,通常是 GitHub 仓库:
"repository": {
"type": "git",
"url": "https://github.com/facebook/react.git"
}
9. files
(文件)
files
字段指定哪些文件会被发布到 npm
仓库,帮助减小包的体积:
"files": [
"LICENSE",
"README.md",
"index.js",
"dist/"
]
10. main
(入口文件)和 browser
(浏览器)
main
字段指定包的入口文件,默认值为 index.js
。如果包用于浏览器环境,可以使用 browser
字段:
"main": "dist/index.js",
"browser": "dist/browser.js"
11. module
(ES 模块入口)
module
字段指定支持 ES 模块的入口文件,优先于 main
字段:
"module": "./dist/index.esm.js"
12. bin
(可执行文件)
如果包包含命令行工具,可以通过 bin
字段指定可执行文件:
"bin": {
"my-cli": "./bin/cli.js"
}
13. scripts
(脚本)
scripts
字段定义了可以通过 npm run <command>
执行的命令。例如,dev
命令可以通过 npm run dev
执行:
"scripts": {
"dev": "node index.js",
"predev": "node pre.js",
"postdev": "node post.js"
}
执行顺序:predev
→ dev
→ postdev
。
14. dependencies
(生产依赖)
dependencies
字段定义了项目运行时需要的依赖包,例如:
"dependencies": {
"react": "^18.2.0",
"axios": "^1.2.6"
}
15. devDependencies
(开发依赖)
devDependencies
字段定义了开发时需要的依赖包,例如:
"devDependencies": {
"webpack": "^5.0.0",
"eslint": "^7.32.0"
}
16. peerDependencies
(对等依赖)
peerDependencies
字段用于指定插件或库依赖的宿主环境的包和版本:
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
17. optionalDependencies
(可选依赖)
optionalDependencies
字段定义了可选依赖,即使安装失败,npm
也认为安装过程成功:
"optionalDependencies": {
"optional-package": "^1.0.0"
}
18. private
(私有)
private
字段防止包被意外发布到 npm
上:
"private": true
19. engines
(环境要求)
engines
字段指定包的 Node.js 和 npm 版本要求:
"engines": {
"node": ">=14.0.0",
"npm": ">=6.0.0"
}
20. workspaces
(工作区)
workspaces
字段用于管理多个相关包,在 monorepo 中很常见:
"workspaces": [
"packages/*",
"apps/*"
]
最佳实践
-
版本控制:
- 使用语义化版本号;
- 明确指定依赖版本,避免使用
*
或latest
; - 使用
package-lock.json
锁定依赖版本。
-
依赖管理:
- 区分生产依赖和开发依赖;
- 合理使用对等依赖;
- 定期更新过期依赖;
- 进行依赖安全审计 (
npm audit
)。
-
脚本管理:
- 保持脚本命名的一致性;
- 合理使用前置/后置钩子;
- 添加注释说明复杂脚本的用途。
-
发布相关:
- 使用
.npmignore
或files
字段控制发布内容; - 设置合适的发布配置;
- 私有包设置
private: true
。
- 使用
-
文档维护:
- 保持
README.md
更新; - 明确标注版本要求和使用说明;
- 提供示例代码。
- 保持
-
性能优化:
- 使用
sideEffects
帮助打包工具进行 tree-shaking; - 提供多种模块格式(CommonJS 和 ESM)支持不同环境;
- 使用
bundledDependencies
减少安装时间。
- 使用
-
兼容性管理:
- 使用
browserslist
明确支持的浏览器范围; - 通过
engines
字段指定 Node.js 版本要求; - 使用
exports
提供不同环境的入口点。
- 使用
通过这些最佳实践,你可以更好地管理你的 Node.js 项目,确保代码质量和可维护性。