本文将带你从零开始,完成一个 npm 包的开发、测试、发布、更新及维护的全流程。每一步都配有真实命令和代码示例,确保你不仅能发布,还能专业地管理整个生命周期。
目录
- 前期准备
- 创建并初始化包
- 编写包代码与入口文件
- 配置
package.json核心字段 - 编写 README 与文档
- 添加开源许可证
- 配置
.npmignore与发布文件 - 本地测试与链接
- 语义化版本管理
- 发布到 npm 仓库
- 更新与重新发布
- 发布预发布版本(beta/alpha)
- 弃用与撤销包
- 发布私有包(付费组织)
- 工作中使用本地私有包
- 安全最佳实践
- 常见问题与排查
- 完整示例项目
1. 前期准备
1.1 注册 npm 账号
访问 npmjs.com 注册账号,或通过命令行注册。
命令行注册:
npm adduser
# 根据提示输入用户名、密码、邮箱
命令行登录(已有账号):
npm login
# 输入用户名、密码、邮箱(邮箱会收到一次性验证码)
1.2 验证登录状态
npm whoami
# 输出你的用户名,如:zhangsan
如果未登录会报错 npm ERR! code E401。
1.3 配置 npm 源
确保使用官方源(如果要发布到公共仓库):
npm config get registry
# 必须输出 https://registry.npmjs.org/
如果之前换成了淘宝源,请临时切换:
npm config set registry https://registry.npmjs.org/
发布完成后可切换回来。
1.4 准备必要的环境
- Node.js(建议使用 LTS 版本,如 18+)
- Git(可选,但推荐用于版本管理)
node -v # v18.17.0
npm -v # 9.6.7
git --version
2. 创建并初始化包
创建一个新目录,并进入:
mkdir my-awesome-lib
cd my-awesome-lib
2.1 初始化 package.json
使用 npm init 交互式创建:
npm init
根据提示填写:
package name: (my-awesome-lib) my-awesome-lib
version: (1.0.0) 1.0.0
description: A library to do awesome things
entry point: (index.js) index.js
test command: jest
git repository: https://github.com/zhangsan/my-awesome-lib
keywords: awesome, utility
author: Zhang San <zhangsan@example.com>
license: (ISC) MIT
快速创建(使用默认值,之后手动修改):
npm init -y
2.2 创建基础目录结构
my-awesome-lib/
├── index.js # 入口文件
├── lib/ # 核心代码目录
│ └── core.js
├── test/ # 测试目录
│ └── core.test.js
├── README.md
├── LICENSE
├── .gitignore
├── .npmignore # 发布时忽略的文件
└── package.json
创建目录和文件:
mkdir lib test
touch index.js lib/core.js test/core.test.js README.md .gitignore
3. 编写包代码与入口文件
3.1 编写模块代码
编写 lib/core.js(简单示例:一个加法工具):
// lib/core.js
/**
* Adds two numbers
* @param {number} a - first number
* @param {number} b - second number
* @returns {number} sum
*/
function add(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Arguments must be numbers');
}
return a + b;
}
/**
* Subtracts two numbers
* @param {number} a
* @param {number} b
* @returns {number} difference
*/
function subtract(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Arguments must be numbers');
}
return a - b;
}
module.exports = { add, subtract };
3.2 编写入口文件 index.js
// index.js
const { add, subtract } = require('./lib/core');
module.exports = {
add,
subtract,
// 其他可能会暴露的 API
};
3.3 编写单元测试(使用 Jest)
安装 Jest(开发依赖):
npm install --save-dev jest
在 package.json 中添加测试脚本:
"scripts": {
"test": "jest"
}
编写 test/core.test.js:
const { add, subtract } = require('../lib/core');
test('adds 1 + 2 to equal 3', () => {
expect(add(1, 2)).toBe(3);
});
test('subtracts 5 - 3 to equal 2', () => {
expect(subtract(5, 3)).toBe(2);
});
test('add throws on non-numbers', () => {
expect(() => add('1', 2)).toThrow(TypeError);
});
运行测试:
npm test
3.4 添加构建步骤(可选)
如果包需要转译(TypeScript, Babel),可以配置构建脚本。常见方式:
"scripts": {
"build": "babel src -d dist",
"prepublishOnly": "npm run build"
}
prepublishOnly 会在 npm publish 前自动运行,确保发布最新构建产物。
4. 配置 package.json 核心字段
一个完善的 package.json 能提升包的可用性和可信度。下面详解关键字段。
4.1 基础字段
{
"name": "my-awesome-lib",
"version": "1.0.0",
"description": "A library to do awesome things",
"main": "index.js",
"keywords": ["awesome", "utility", "math"],
"author": "Zhang San <zhangsan@example.com> (https://zhangsan.dev)",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/zhangsan/my-awesome-lib.git"
},
"bugs": {
"url": "https://github.com/zhangsan/my-awesome-lib/issues"
},
"homepage": "https://github.com/zhangsan/my-awesome-lib#readme"
}
4.2 files – 控制发布内容
指定哪些文件会被打包进 tarball,可以大幅减少包体积。
{
"files": [
"index.js",
"lib/",
"dist/",
"README.md",
"LICENSE"
]
}
没有被列出且没有被 .npmignore 排除的文件会被忽略。默认情况下 npm 会忽略 .git/, .*.swp, *.log 等,但最好显式声明。
4.3 scripts – 生命周期脚本
常用脚本:
{
"scripts": {
"test": "jest",
"build": "babel src -d dist",
"prepublishOnly": "npm run test && npm run build",
"postpublish": "git push origin --tags"
}
}
prepublishOnly:在npm publish前运行,通常用于测试和构建。postpublish:发布后推送到 git 仓库和标签。
4.4 engines – 指定 Node.js 版本
{
"engines": {
"node": ">=14.0.0",
"npm": ">=6.0.0"
}
}
用户安装时若版本不符会收到警告。
4.5 peerDependencies – 对等依赖
如果你的包是一个插件(例如 Vue 插件),需要宿主环境提供某些依赖:
{
"peerDependencies": {
"vue": "^3.2.0"
}
}
4.6 publishConfig – 发布特定配置
{
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "public" // 公开包(默认)
// "access": "restricted" 用于私有包
}
}
4.7 types – TypeScript 类型定义
如果提供 TypeScript 类型,可以指定入口:
{
"types": "index.d.ts"
}
5. 编写 README 与文档
README 是包的门面,建议包含:
- 徽章(版本、测试覆盖率、下载量)
- 安装方式
- 基本用法示例
- API 文档
- 贡献指南
- 许可证说明
示例 README.md:
# my-awesome-lib
[](https://www.npmjs.com/package/my-awesome-lib)
[](https://opensource.org/licenses/MIT)
A simple library for math operations.
## Install
```bash
npm install my-awesome-lib
Usage
const { add, subtract } = require('my-awesome-lib');
console.log(add(5, 3)); // 8
console.log(subtract(5, 3)); // 2
API
add(a, b)
Returns the sum of two numbers.
subtract(a, b)
Returns the difference of two numbers.
License
MIT
---
## 6. 添加开源许可证
在项目根目录创建 `LICENSE` 文件。常用的 MIT 许可证内容:
```text
MIT License
Copyright (c) 2025 Zhang San
Permission is hereby granted, free of charge, to any person obtaining a copy
...
也可以在 GitHub 上创建仓库时自动生成。
7. 配置 .npmignore 与发布文件
7.1 为什么需要 .npmignore
类似 .gitignore,但用于告诉 npm 哪些文件不要发布。如果没有 .npmignore,npm 会使用 .gitignore 规则(可能在 npm publish 时意外忽略必要文件)。最佳实践:同时使用 .npmignore 显式控制发布内容。
7.2 示例 .npmignore
# 源码目录(如果使用构建,则只发布 dist)
src/
test/
coverage/
# 开发工具配置文件
.babelrc
.eslintrc
.prettierrc
.gitignore
.editorconfig
# CI 配置
.travis.yml
.github/
# 日志
*.log
npm-debug.log*
# 临时文件
*.swp
*.tmp
# 测试报告
coverage/
# 其他
node_modules/
.DS_Store
7.3 验证将要发布的文件
使用 npm pack 打包成 tarball 并查看内容:
npm pack --dry-run
# 会列出包含的文件
# 也可以实际生成 .tgz 文件查看
npm pack
# 生成 my-awesome-lib-1.0.0.tgz
tar -tzf my-awesome-lib-1.0.0.tgz
确认无误后删除生成的 .tgz 文件:
rm my-awesome-lib-1.0.0.tgz
8. 本地测试与链接
8.1 在本地模拟安装(通过 npm link)
步骤1:在包的根目录创建全局链接:
cd /path/to/my-awesome-lib
npm link
# 创建软链接到全局 node_modules
步骤2:在另一个测试项目中链接该包:
cd /path/to/test-project
npm link my-awesome-lib
# 此时 test-project/node_modules/my-awesome-lib 会指向原开发目录
步骤3:在测试项目中写代码测试:
const lib = require('my-awesome-lib');
console.log(lib.add(2,3)); // 5
步骤4:修改原包代码,测试项目中的引用会立即生效(无需重新安装)。
解除链接:
cd test-project
npm unlink my-awesome-lib # 解除项目中的链接
cd /path/to/my-awesome-lib
npm unlink # 删除全局链接
8.2 使用本地路径安装
也可以直接使用相对路径安装(不创建全局链接):
cd /path/to/test-project
npm install ../my-awesome-lib
会在 package.json 中产生:
"dependencies": {
"my-awesome-lib": "file:../my-awesome-lib"
}
这种方式不适合最终发布,但本地调试很方便。
8.3 在测试项目中运行测试
确保你的包在真实安装环境下工作正常。
9. 语义化版本管理
npm 严格遵循 Semantic Versioning 2.0.0。
9.1 手动修改版本号
直接编辑 package.json 的 version 字段。
9.2 使用 npm version 命令
自动升级版本并创建 git 标签。
# 补丁版本 1.0.0 -> 1.0.1 (bug修复)
npm version patch
# 次要版本 1.0.0 -> 1.1.0 (新增功能,向下兼容)
npm version minor
# 主要版本 1.0.0 -> 2.0.0 (不兼容改动)
npm version major
# 指定版本号
npm version 2.0.0
默认情况下 npm version 会:
- 修改
package.json的version - 提交一个 git commit(commit message 为版本号)
- 创建一个 git tag(例如
v1.0.1)
跳过 git 操作(例如 CI 环境):
npm version patch --no-git-tag-version --no-commit-hooks
自定义 commit 消息:
npm version patch -m "chore(release): %s"
# %s 会被替换为版本号
9.3 推送标签到远程仓库
git push origin main --tags
可以结合 postversion 脚本自动推送:
"scripts": {
"postversion": "git push && git push --tags"
}
10. 发布到 npm 仓库
10.1 发布前检查清单
- 所有测试通过:
npm test - 构建产物已生成:
npm run build(如果需要) -
package.json的name未被占用(可提前在 npm 官网搜索) - 版本号正确
- 登录状态有效:
npm whoami - registry 指向官方源:
npm config get registry - 发布的文件列表正确:
npm pack --dry-run
10.2 执行发布
npm publish
如果是第一次发布一个作用域包(如 @mycompany/my-lib),默认会被视为私有包(需要付费)。要发布为公共包,需指定:
npm publish --access public
或者在 package.json 的 publishConfig 中设置:
"publishConfig": {
"access": "public"
}
10.3 发布后验证
- 访问
https://www.npmjs.com/package/<package-name>查看页面。 - 尝试在一个新目录安装:
mkdir test-install && cd test-install npm init -y npm install <package-name>
10.4 使用 --dry-run 模拟发布
npm publish --dry-run
只显示发布行为,不实际发送。
11. 更新与重新发布
11.1 更新包代码
修改代码、编写测试、更新文档。
11.2 变更日志(CHANGELOG.md)
建议维护 CHANGELOG.md:
# Changelog
## [1.1.0] - 2025-01-15
### Added
- New function `multiply`
### Fixed
- Bug in subtract when arguments are negative
## [1.0.1] - 2025-01-10
### Fixed
- TypeError handling in add function
11.3 升级版本并发布
# 方式1:手动修改 package.json 版本,然后发布
npm publish
# 方式2:使用 npm version 自动升级
npm version patch # 或 minor, major
git push origin main --tags
npm publish
11.4 使用 --otp 提供两步验证码
如果你的账号开启了 2FA(双因素认证),发布时需要提供 OTP:
npm publish --otp=123456
也可以在 npm login 时保存 OTP 会话。
12. 发布预发布版本(beta/alpha)
用于在正式发布前进行测试。
12.1 预发布版本号规则
- 1.0.0 -> 1.0.1-beta.0
- 1.0.0 -> 1.1.0-alpha.1
12.2 使用 npm version 的预发布命令
# 从 1.0.0 创建预发布版本 1.0.1-0 (如果未指定标识符,默认为 prerelease)
npm version prerelease
# 创建 beta 预发布版 1.0.1-beta.0
npm version prepatch --preid=beta # 1.0.1-beta.0
# 再次运行: 1.0.1-beta.1
# 创建 alpha 预发布版 1.1.0-alpha.0
npm version preminor --preid=alpha # 1.1.0-alpha.0
# 创建 major 预发布 2.0.0-alpha.0
npm version premajor --preid=alpha
12.3 打标签并发布
发布预发布版本时使用 --tag 指定标签(默认为 latest):
npm publish --tag beta
用户安装时需指定标签:
npm install my-awesome-lib@beta
12.4 从预发布转为正式版
# 假设当前是 1.0.1-beta.2,想发布 1.0.1
npm version patch --no-git-tag-version # 手动改为 1.0.1? 不推荐
# 更好的做法:
npm version patch # 会升级到 1.0.1 (去掉预发布标识)
npm publish
13. 弃用与撤销包
13.1 弃用某个版本(推荐)
弃用后,用户安装该版本时会收到警告,但仍可下载。
npm deprecate my-awesome-lib@"<1.2.0" "This version has a critical bug, please upgrade to 1.2.0"
# 弃用整个包(所有版本)
npm deprecate my-awesome-lib "This package is no longer maintained"
13.2 撤销已发布的版本(24 小时内)
注意:只有发布后 72 小时内(不同文档说法有出入,官方为 72 小时,但实际操作有时限制 24 小时)可以撤销。撤销后该版本不可再被安装。
npm unpublish my-awesome-lib@1.0.1
撤销整个包(需要联系 npm 团队或使用 --force 且包发布时间很长,不推荐):
npm unpublish my-awesome-lib --force
最佳实践:用 npm deprecate 代替 unpublish,以免破坏依赖此包的项目。
14. 发布私有包(付费组织)
14.1 创建付费组织
访问 npmjs.com 创建组织(需要付费订阅),例如 @mycompany。
14.2 配置作用域包
在 package.json 中设置包名:
{
"name": "@mycompany/awesome-lib"
}
14.3 配置私有仓库访问
登录到组织作用域:
npm login --scope=@mycompany --registry=https://registry.npmjs.org/
在项目 .npmrc 或用户 .npmrc 中设置:
@mycompany:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=<your-token>
14.4 发布私有包
npm publish --access restricted
或在 package.json 中:
"publishConfig": {
"access": "restricted"
}
之后只有组织成员可以安装该包。安装时需登录。
15. 工作中使用本地私有包
开发时频繁修改的私有包,可以采用以下工作流:
15.1 使用 npm link(开发阶段)
如上第 8 节所述。
15.2 使用 npm install <git-url>
如果将代码放在私有 Git 仓库,可以直接从 Git 安装:
npm install git+https://github.com/mycompany/awesome-lib.git
# 或 SSH
npm install git+ssh://git@github.com:mycompany/awesome-lib.git
15.3 发布到私有 npm 仓库(Verdaccio)
搭建内部 npm 服务(如 Verdaccio),配置 registry 指向它,然后发布。
npm set registry http://localhost:4873/
npm publish
16. 安全最佳实践
16.1 不要发布敏感信息
- 避免在代码中硬编码 API 密钥、密码。
- 检查
.npmignore排除了.env、secrets/等。 - 使用
npm pack --dry-run检查将要发布的文件。
16.2 启用两步验证(2FA)
在 npm 网站账户设置中开启 2FA,并选择“授权和发布”都需要验证。
16.3 使用自动化 token
在 CI 中发布时,使用 npm 网站生成的 Automation token(权限受限,只能发布特定包)。
npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
16.4 定期审查依赖
npm audit
npm outdated
16.5 签名提交与标签
配置 git 使用 GPG 签名,让发布的 tag 可验证。
17. 常见问题与排查
17.1 发布时提示 “You do not have permission to publish”
- 包名已被别人占用,更换
name。 - 未登录:
npm login - 作用域包未设置
--access public
17.2 发布时提示 “Package version already exists”
- 版本号未递增,修改
version后再发布。
17.3 安装自己的包时报错 “403 Forbidden”
- 私有包未登录,执行
npm login --scope=@mycompany
17.4 发布时文件太大
- 检查
.npmignore是否忽略了node_modules、test、src等不必要文件。 - 使用
files字段只包含必要目录。
17.5 发布后其他项目安装的版本不是最新
- 检查
npm view <pkg> version确认最新版本。 - 用户可能使用了
npm ci或锁文件,需要更新 lock 文件。
17.6 撤销发布后 24 小时之外怎么办?
- 不能撤销,只能发布新版本修复,或弃用旧版本。
18. 完整示例项目
下面是一个完整的示例包的目录和关键文件。
项目结构:
my-awesome-lib/
├── .gitignore
├── .npmignore
├── index.js
├── lib/
│ └── core.js
├── test/
│ └── core.test.js
├── CHANGELOG.md
├── README.md
├── LICENSE
└── package.json
.gitignore:
node_modules/
coverage/
.DS_Store
*.log
.env
dist/
.npmignore:
src/ # 如果有源文件目录
test/
coverage/
.*.swp
.*.tmp
.gitignore
.eslintrc
.prettierrc
package.json 完整示例:
{
"name": "my-awesome-lib",
"version": "1.0.0",
"description": "A library to do awesome things",
"main": "index.js",
"scripts": {
"test": "jest",
"prepublishOnly": "npm test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/zhangsan/my-awesome-lib.git"
},
"keywords": ["awesome", "utility"],
"author": "Zhang San <zhangsan@example.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/zhangsan/my-awesome-lib/issues"
},
"homepage": "https://github.com/zhangsan/my-awesome-lib#readme",
"devDependencies": {
"jest": "^29.7.0"
},
"engines": {
"node": ">=14.0.0"
},
"files": [
"index.js",
"lib/",
"README.md",
"LICENSE"
]
}
发布命令序列:
# 确保版本正确
npm version minor # 升级版本
# 运行测试
npm test
# 发布前打包检查
npm pack --dry-run
# 发布
npm publish
# 推送 tags
git push origin main --tags
安装与验证:
npm install my-awesome-lib
node -e "console.log(require('my-awesome-lib').add(2,3))"
# 输出 5
总结
发布一个 npm 包并不复杂,但要做到专业、安全、易维护,需要关注每个细节。遵循本文的步骤,你将能:
- 正确配置包结构
- 编写清晰文档
- 测试并构建
- 语义化版本管理
- 发布及更新
- 处理预发布和废弃
现在你可以将自己的 Node.js 模块分享给全世界了!🚀