npm 发布自己的包操作完全指南(超详细版)

3 阅读13分钟

本文将带你从零开始,完成一个 npm 包的开发、测试、发布、更新及维护的全流程。每一步都配有真实命令和代码示例,确保你不仅能发布,还能专业地管理整个生命周期。


目录

  1. 前期准备
  2. 创建并初始化包
  3. 编写包代码与入口文件
  4. 配置 package.json 核心字段
  5. 编写 README 与文档
  6. 添加开源许可证
  7. 配置 .npmignore 与发布文件
  8. 本地测试与链接
  9. 语义化版本管理
  10. 发布到 npm 仓库
  11. 更新与重新发布
  12. 发布预发布版本(beta/alpha)
  13. 弃用与撤销包
  14. 发布私有包(付费组织)
  15. 工作中使用本地私有包
  16. 安全最佳实践
  17. 常见问题与排查
  18. 完整示例项目

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

[![npm version](https://badge.fury.io/js/my-awesome-lib.svg)](https://www.npmjs.com/package/my-awesome-lib)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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.jsonversion 字段。

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.jsonversion
  • 提交一个 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.jsonname 未被占用(可提前在 npm 官网搜索)
  • 版本号正确
  • 登录状态有效:npm whoami
  • registry 指向官方源:npm config get registry
  • 发布的文件列表正确:npm pack --dry-run

10.2 执行发布

npm publish

如果是第一次发布一个作用域包(如 @mycompany/my-lib),默认会被视为私有包(需要付费)。要发布为公共包,需指定:

npm publish --access public

或者在 package.jsonpublishConfig 中设置:

"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 排除了 .envsecrets/ 等。
  • 使用 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_modulestestsrc 等不必要文件。
  • 使用 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 模块分享给全世界了!🚀