随着前端项目复杂度的提升,模块化开发已成为标配。NPM(Node Package Manager)作为 JavaScript 生态系统中最大的包管理工具,不仅让我们能够便捷地使用他人的代码,也为我们提供了分享自己代码的平台。
本文将从零开始,循序渐进地介绍如何发布和管理 npm 包,涵盖从入门到进阶的完整知识体系,让初学者能够快速上手,也让有经验的开发者能够查漏补缺。
# 一、快速开始:发布你的第一个 npm 包
# 1.1 前置准备
在发布 npm 包之前,你需要完成以下准备工作:
# 注册 npm 账号
访问 npmjs.com (opens new window) 注册一个账号。注册完成后,在本地登录:
npm login
按提示输入用户名、密码和邮箱。登录成功后,可以通过以下命令确认:
npm whoami
# 初始化项目
使用 npm init 创建一个新项目:
mkdir my-awesome-package
cd my-awesome-package
npm init
npm init 会以交互式方式引导你创建 package.json 文件。如果想跳过交互,使用 -y 参数:
npm init -y
这会使用默认值创建 package.json。
# 1.2 最小化配置
要发布一个 npm 包,package.json 中只有两个字段是必需的:
{
"name": "my-awesome-package",
"version": "1.0.0"
}
# name - 包名
包名需要遵循以下规则:
- 唯一性:在 npm registry 中不能与现有包重名
- 长度限制:不超过 214 个字符
- 命名规范:
- 只能包含小写字母、数字、连字符
-和下划线_ - 不能以
.或_开头 - 不能包含非 URL 安全字符
- 不能与 Node.js 核心模块同名
- 只能包含小写字母、数字、连字符
检查包名是否可用:
npm view <package-name>
如果显示 404,说明该名称可用。
作用域包(Scoped Packages)
为了避免命名冲突,可以使用作用域包:
{
"name": "@your-username/my-awesome-package"
}
作用域包默认是私有的。要发布公开的作用域包,需要使用:
npm publish --access public
# version - 版本号
版本号遵循 Semantic Versioning (opens new window)(语义化版本)规范,格式为 X.Y.Z:
- X(主版本号 Major):不兼容的 API 修改
- Y(次版本号 Minor):向下兼容的功能性新增
- Z(修订号 Patch):向下兼容的问题修正
示例:
1.0.0→1.0.1:修复了一个 bug1.0.0→1.1.0:新增了一个功能1.0.0→2.0.0:有破坏性的改动
# 1.3 发布流程
# 创建入口文件
创建一个 index.js 文件作为包的入口:
// index.js
function sayHello(name) {
return `Hello, ${name}!`;
}
module.exports = { sayHello };
# 测试发布
在正式发布前,建议先测试一下:
# 查看将要发布的文件列表
npm pack --dry-run
# 或者实际打包查看
npm pack
# 发布到 npm
npm publish
首次发布作用域包时:
npm publish --access public
发布成功后,可以在 npmjs.com (opens new window) 上搜索到你的包。
# 快速示例
让我们看一个完整的最小化示例:
项目结构:
my-hello-package/
├── index.js
├── package.json
└── README.md
package.json:
{
"name": "@username/my-hello-package",
"version": "1.0.0",
"description": "A simple hello package",
"main": "index.js",
"keywords": ["hello", "demo"],
"author": "Your Name",
"license": "MIT"
}
index.js:
exports.sayHello = (name = 'World') => `Hello, ${name}!`;
README.md:
# My Hello Package
A simple package that says hello.
## Installation
\`\`\`bash
npm install @username/my-hello-package
\`\`\`
## Usage
\`\`\`javascript
const { sayHello } = require('@username/my-hello-package');
console.log(sayHello('NPM'));
\`\`\`
发布命令:
npm publish --access public
# 二、进阶配置:让你的包更专业
# 2.1 版本管理策略
手动修改版本号容易出错,使用 npm 提供的版本管理命令更安全:
# 修订号 +1 (1.0.0 -> 1.0.1)
npm version patch
# 次版本号 +1 (1.0.0 -> 1.1.0)
npm version minor
# 主版本号 +1 (1.0.0 -> 2.0.0)
npm version major
这些命令会:
- 修改
package.json中的版本号 - 自动创建 git commit
- 打上 git tag
完整的发布流程:
# 1. 更新版本
npm version patch
# 2. 推送代码和标签
git push && git push --tags
# 3. 发布到 npm
npm publish
# 预发布版本
对于测试版本,使用预发布标识:
# 发布 alpha 版本 (1.0.0-alpha.0)
npm version prerelease --preid=alpha
# 发布 beta 版本 (1.0.0-beta.0)
npm version prerelease --preid=beta
# 发布 rc 版本 (1.0.0-rc.0)
npm version prerelease --preid=rc
用户可以通过标签安装特定版本:
# 安装最新稳定版
npm install my-package
# 安装 beta 版本
npm install my-package@beta
# 安装特定版本
npm install my-package@1.0.0-beta.0
发布预发布版本时使用标签:
npm publish --tag beta
# 2.2 文件管理
默认情况下,npm 会发布项目中的所有文件(除了某些内置忽略的文件)。为了控制发布内容,有三种方式:
# 方式一:使用 files 字段(推荐)
在 package.json 中指定要发布的文件:
{
"files": [
"dist",
"lib",
"src",
"README.md",
"LICENSE"
]
}
这是白名单机制,只有列出的文件和目录会被发布。
注意:以下文件会始终被包含,无需配置:
package.jsonREADME.mdLICENSEmain字段指定的文件
# 方式二:使用 .npmignore
类似 .gitignore,指定不发布的文件:
# .npmignore
node_modules
src
tests
*.test.js
.eslintrc
这是黑名单机制。
优先级:
- 如果存在
files字段,.npmignore会被忽略 - 如果没有
files字段但有.npmignore,则使用.npmignore - 如果都没有,则使用
.gitignore
# 方式三:使用 .gitignore
如果既没有 files 也没有 .npmignore,npm 会使用 .gitignore 的规则。
# 最佳实践
- 优先使用
files字段:更明确,不易出错 - 查看发布内容:发布前使用
npm pack --dry-run检查 - 精简发布:只发布必要文件,减小包体积
# 查看将被发布的文件
npm pack --dry-run
# 实际打包到 .tgz 文件
npm pack
# 查看打包内容
tar -tzf your-package-1.0.0.tgz
# 2.3 入口文件配置
入口文件配置决定了用户如何引入你的包,这对包的使用体验至关重要。
# main - 主入口(必需)
main 字段指定包的主入口文件,默认是 index.js:
{
"main": "dist/index.js"
}
当用户使用 require('your-package') 时,会加载这个文件。
# module - ES Module 入口
module 字段指定 ES Module 格式的入口文件:
{
"main": "dist/index.js", // CommonJS 入口
"module": "dist/index.esm.js" // ES Module 入口
}
支持 ES Module 的打包工具(如 Webpack、Rollup)会优先使用 module 字段,以便进行 Tree Shaking 优化。
# browser - 浏览器入口
为浏览器环境指定特殊的入口:
{
"main": "dist/index.js",
"browser": "dist/index.browser.js"
}
或者针对不同文件做映射:
{
"browser": {
"./lib/server.js": "./lib/browser.js",
"fs": false
}
}
# exports - 现代化的入口配置(推荐)
exports 是最现代的入口配置方式,优先级最高:
基础用法:
{
"exports": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
}
}
等同于:
{
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
}
}
}
多环境支持:
{
"exports": {
".": {
"node": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
},
"browser": {
"require": "./dist/browser.cjs",
"import": "./dist/browser.mjs"
},
"default": "./dist/index.js"
}
}
}
子路径导出:
{
"exports": {
".": "./dist/index.js",
"./utils": "./dist/utils.js",
"./style.css": "./dist/style.css",
"./package.json": "./package.json"
}
}
用户可以这样使用:
import pkg from 'your-package'; // 主入口
import { format } from 'your-package/utils'; // 子路径
import 'your-package/style.css'; // 样式文件
封装性:exports 未定义的路径无法被外部访问:
// ❌ 报错:不在 exports 中定义
import internal from 'your-package/dist/internal/secret';
条件导出的执行顺序:
{
"exports": {
".": {
"types": "./dist/index.d.ts", // TypeScript 类型
"node": "./dist/index.node.js", // Node.js 环境
"browser": "./dist/index.browser.js", // 浏览器环境
"import": "./dist/index.mjs", // ES Module
"require": "./dist/index.cjs", // CommonJS
"default": "./dist/index.js" // 兜底
}
}
}
优先级顺序(从高到低):
exportsmodule/browser(取决于环境)main
# 完整示例
一个支持多种环境的现代包配置:
{
"name": "my-universal-package",
"version": "1.0.0",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"node": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"browser": {
"import": "./dist/browser.mjs",
"require": "./dist/browser.cjs"
},
"default": "./dist/index.js"
},
"./utils": {
"types": "./dist/utils.d.ts",
"import": "./dist/utils.mjs",
"require": "./dist/utils.cjs"
},
"./style.css": "./dist/style.css",
"./package.json": "./package.json"
},
"files": ["dist"]
}
# 2.4 类型定义配置
# types / typings
为 TypeScript 用户提供类型定义:
{
"types": "dist/index.d.ts",
"main": "dist/index.js"
}
或者发布到 @types 组织:
# 用户安装你的包和对应的类型定义
npm install your-package
npm install --save-dev @types/your-package
# 在 exports 中配置类型
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
# 2.5 可执行脚本配置
# bin - 命令行工具
如果你的包提供命令行工具,使用 bin 字段:
{
"name": "my-cli-tool",
"bin": {
"mycli": "./bin/cli.js"
}
}
或者包名和命令名相同时:
{
"name": "my-cli-tool",
"bin": "./bin/cli.js"
}
cli.js 文件格式:
#!/usr/bin/env node
console.log('Hello from CLI!');
必须以 #!/usr/bin/env node 开头,并确保文件有执行权限:
chmod +x bin/cli.js
用户全局安装后即可使用:
npm install -g my-cli-tool
mycli
本地安装时通过 npx 使用:
npm install my-cli-tool
npx mycli
# 2.6 脚本配置
# scripts
定义项目的脚本命令:
{
"scripts": {
"build": "rollup -c",
"test": "jest",
"lint": "eslint src",
"format": "prettier --write \"src/**/*.js\"",
"prepublishOnly": "npm run build && npm test"
}
}
生命周期脚本:
npm 在特定时机会自动执行某些脚本:
{
"scripts": {
"preinstall": "echo 'Installing...'",
"install": "node-gyp rebuild",
"postinstall": "echo 'Installed!'",
"prepublish": "npm run build", // npm v7+ 已废弃
"prepublishOnly": "npm run build", // 推荐使用
"prepack": "npm run build",
"postpack": "echo 'Packed!'",
"preversion": "npm test",
"version": "npm run format && git add -A",
"postversion": "git push && git push --tags"
}
}
最佳实践脚本:
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\"",
"type-check": "tsc --noEmit",
"prepublishOnly": "npm run lint && npm run type-check && npm run test && npm run build",
"release": "npm version patch && npm publish",
"release:minor": "npm version minor && npm publish",
"release:major": "npm version major && npm publish"
}
}
# 2.7 依赖配置
# dependencies
生产环境依赖,用户安装你的包时会一起安装:
{
"dependencies": {
"lodash": "^4.17.21",
"axios": "~1.6.0"
}
}
版本范围说明:
^1.2.3:兼容 1.x.x,即>=1.2.3 <2.0.0~1.2.3:兼容 1.2.x,即>=1.2.3 <1.3.01.2.3:精确版本*或latest:最新版本>1.2.3、>=1.2.3、<1.2.3、<=1.2.3:范围1.2.3 - 1.5.0:范围
# devDependencies
开发环境依赖,不会安装给用户:
{
"devDependencies": {
"typescript": "^5.3.0",
"jest": "^29.7.0",
"eslint": "^8.56.0",
"@types/node": "^20.10.0"
}
}
# peerDependencies
声明宿主环境需要的依赖:
{
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
}
使用场景:
- React 组件库需要 React
- Vue 插件需要 Vue
- Webpack 插件需要 Webpack
npm 7+ 行为变化:
- npm 7+ 会自动安装 peerDependencies
- npm 3-6 只会发出警告
# peerDependenciesMeta
标记可选的 peer dependencies:
{
"peerDependencies": {
"react": ">=16.8.0",
"vue": ">=3.0.0"
},
"peerDependenciesMeta": {
"vue": {
"optional": true
}
}
}
表示包可以同时支持 React 和 Vue,但 Vue 是可选的。
# optionalDependencies
可选依赖,安装失败不影响包的使用:
{
"optionalDependencies": {
"fsevents": "^2.3.2"
}
}
代码中需要做兼容处理:
let fsevents;
try {
fsevents = require('fsevents');
} catch (err) {
// 不存在时的降级方案
}
# bundledDependencies
打包依赖,发布时会包含在 tarball 中:
{
"bundledDependencies": [
"my-private-package"
]
}
# overrides(npm 8.3+)
强制覆盖间接依赖的版本:
{
"overrides": {
"lodash": "^4.17.21",
"foo": {
"bar": "1.0.0"
}
}
}
# 2.8 发布配置
# private
防止意外发布私有包:
{
"private": true
}
设置为 true 后,npm publish 会失败。
# publishConfig
发布时的配置:
{
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/",
"tag": "latest"
}
}
常用配置:
access:public或restricted(作用域包默认)registry:发布到的仓库地址tag:发布标签(默认latest)
# 2.9 系统配置
# engines
指定运行环境的版本要求:
{
"engines": {
"node": ">=18.0.0",
"npm": ">=9.0.0"
}
}
注意:这只是建议,不会强制执行。要强制执行,需要配置 .npmrc:
engine-strict=true
# os
指定操作系统:
{
"os": ["darwin", "linux"], // 允许
"os": ["!win32"] // 禁止
}
# cpu
指定 CPU 架构:
{
"cpu": ["x64", "arm64"],
"cpu": ["!arm", "!mips"]
}
# 2.10 第三方工具配置
# sideEffects(Webpack)
标记是否有副作用,用于 Tree Shaking:
{
"sideEffects": false
}
或指定有副作用的文件:
{
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfills.js"
]
}
# unpkg / jsdelivr
指定 CDN 入口:
{
"unpkg": "dist/index.umd.js",
"jsdelivr": "dist/index.umd.js"
}
用户可以通过 CDN 使用:
<script src="https://unpkg.com/your-package"></script>
<script src="https://cdn.jsdelivr.net/npm/your-package"></script>
# browserslist
指定浏览器兼容范围:
{
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
# 三、不同类型包的最佳实践
# 3.1 纯 JavaScript 库
适用于通用工具库,如日期处理、字符串处理等。
项目结构:
my-js-library/
├── src/
│ ├── index.js
│ └── utils.js
├── dist/
│ ├── index.js # CommonJS
│ ├── index.mjs # ES Module
│ └── index.umd.js # UMD (浏览器)
├── test/
│ └── index.test.js
├── .npmignore
├── package.json
└── README.md
package.json:
{
"name": "my-js-library",
"version": "1.0.0",
"description": "A useful JavaScript library",
"main": "dist/index.js",
"module": "dist/index.mjs",
"unpkg": "dist/index.umd.js",
"jsdelivr": "dist/index.umd.js",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"browser": "./dist/index.umd.js"
}
},
"files": ["dist"],
"scripts": {
"build": "rollup -c",
"test": "jest",
"prepublishOnly": "npm test && npm run build"
},
"keywords": ["utility", "helper"],
"author": "Your Name",
"license": "MIT",
"devDependencies": {
"rollup": "^4.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"jest": "^29.0.0"
}
}
rollup.config.js:
export default [
{
input: 'src/index.js',
output: [
{ file: 'dist/index.js', format: 'cjs' },
{ file: 'dist/index.mjs', format: 'esm' },
{ file: 'dist/index.umd.js', format: 'umd', name: 'MyLibrary' }
]
}
];
# 3.2 TypeScript 库
现代包推荐使用 TypeScript 开发,提供更好的类型支持。
项目结构:
my-ts-library/
├── src/
│ ├── index.ts
│ ├── types.ts
│ └── utils.ts
├── dist/
│ ├── index.js
│ ├── index.mjs
│ ├── index.d.ts
│ └── types.d.ts
├── test/
│ └── index.test.ts
├── tsconfig.json
├── package.json
└── README.md
package.json:
{
"name": "my-ts-library",
"version": "1.0.0",
"description": "A TypeScript library",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"files": ["dist"],
"scripts": {
"build": "npm run build:types && npm run build:js",
"build:types": "tsc --emitDeclarationOnly",
"build:js": "rollup -c",
"test": "jest",
"type-check": "tsc --noEmit",
"prepublishOnly": "npm run type-check && npm test && npm run build"
},
"keywords": ["typescript", "library"],
"author": "Your Name",
"license": "MIT",
"devDependencies": {
"typescript": "^5.3.0",
"rollup": "^4.0.0",
"@rollup/plugin-typescript": "^11.0.0",
"jest": "^29.0.0",
"@types/jest": "^29.0.0",
"ts-jest": "^29.0.0"
}
}
tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node"
},
"include": ["src"],
"exclude": ["node_modules", "dist", "test"]
}
rollup.config.js:
import typescript from '@rollup/plugin-typescript';
export default [
{
input: 'src/index.ts',
output: [
{ file: 'dist/index.js', format: 'cjs' },
{ file: 'dist/index.mjs', format: 'esm' }
],
plugins: [
typescript({
tsconfig: './tsconfig.json',
declaration: false // 类型声明由 tsc 单独生成
})
]
}
];
# 3.3 CLI 工具
命令行工具需要特殊的配置和结构。
项目结构:
my-cli-tool/
├── bin/
│ └── cli.js
├── src/
│ ├── commands/
│ │ ├── init.js
│ │ └── build.js
│ ├── utils/
│ │ └── logger.js
│ └── index.js
├── templates/
│ └── default/
├── package.json
└── README.md
package.json:
{
"name": "my-cli-tool",
"version": "1.0.0",
"description": "A powerful CLI tool",
"bin": {
"mycli": "./bin/cli.js"
},
"files": ["bin", "src", "templates"],
"scripts": {
"test": "jest",
"prepublishOnly": "npm test"
},
"keywords": ["cli", "tool"],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"commander": "^11.0.0",
"chalk": "^5.3.0",
"inquirer": "^9.2.0",
"ora": "^7.0.0"
},
"devDependencies": {
"jest": "^29.0.0"
},
"engines": {
"node": ">=18.0.0"
}
}
bin/cli.js:
#!/usr/bin/env node
const { program } = require('commander');
const chalk = require('chalk');
const pkg = require('../package.json');
program
.name('mycli')
.description('A powerful CLI tool')
.version(pkg.version);
program
.command('init <project-name>')
.description('Initialize a new project')
.option('-t, --template <template>', 'Template to use')
.action((projectName, options) => {
console.log(chalk.green(`Creating project: ${projectName}`));
// 实现初始化逻辑
});
program
.command('build')
.description('Build the project')
.option('-w, --watch', 'Watch mode')
.action((options) => {
console.log(chalk.blue('Building...'));
// 实现构建逻辑
});
program.parse();
测试 CLI:
# 本地链接
npm link
# 测试命令
mycli --version
mycli init my-project
mycli build --watch
# 取消链接
npm unlink
# 3.4 React 组件库
项目结构:
my-react-components/
├── src/
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.module.css
│ │ │ └── index.ts
│ │ └── Input/
│ ├── styles/
│ │ └── global.css
│ └── index.ts
├── dist/
├── package.json
└── README.md
package.json:
{
"name": "@myorg/react-components",
"version": "1.0.0",
"description": "React component library",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./styles": "./dist/styles.css"
},
"files": ["dist"],
"scripts": {
"build": "vite build && tsc --emitDeclarationOnly",
"dev": "vite",
"prepublishOnly": "npm run build"
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.3.0",
"vite": "^5.0.0",
"vite-plugin-dts": "^3.0.0"
},
"keywords": ["react", "components", "ui"],
"license": "MIT"
}
使用示例:
// 用户代码
import { Button, Input } from '@myorg/react-components';
import '@myorg/react-components/styles';
function App() {
return (
<div>
<Button>Click me</Button>
<Input placeholder="Enter text" />
</div>
);
}
# 3.5 Monorepo 包管理
使用 workspaces 管理多个相关包。
项目结构:
my-monorepo/
├── packages/
│ ├── core/
│ │ ├── src/
│ │ └── package.json
│ ├── utils/
│ │ ├── src/
│ │ └── package.json
│ └── cli/
│ ├── bin/
│ ├── src/
│ └── package.json
├── package.json
└── README.md
根 package.json:
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"packages/*"
],
"scripts": {
"build": "npm run build --workspaces",
"test": "npm run test --workspaces",
"publish:all": "npm publish --workspaces --access public"
},
"devDependencies": {
"typescript": "^5.3.0",
"jest": "^29.0.0"
}
}
packages/core/package.json:
{
"name": "@myorg/core",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc && rollup -c"
}
}
packages/cli/package.json:
{
"name": "@myorg/cli",
"version": "1.0.0",
"bin": {
"myorg": "./bin/cli.js"
},
"dependencies": {
"@myorg/core": "^1.0.0",
"@myorg/utils": "^1.0.0"
}
}
使用 workspaces 命令:
# 安装所有依赖
npm install
# 为某个包添加依赖
npm install lodash --workspace=@myorg/core
# 运行所有包的脚本
npm run build --workspaces
# 发布所有包
npm publish --workspaces --access public
# 3.6 全栈包(Node + Browser)
同时支持 Node.js 和浏览器环境的包。
package.json:
{
"name": "my-universal-package",
"version": "1.0.0",
"description": "Works in Node.js and Browser",
"main": "dist/index.js",
"module": "dist/index.mjs",
"browser": "dist/browser.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"node": {
"import": "./dist/node.mjs",
"require": "./dist/node.js"
},
"browser": {
"import": "./dist/browser.mjs",
"require": "./dist/browser.js"
},
"default": "./dist/index.js"
}
},
"files": ["dist"],
"sideEffects": false
}
src/index.ts:
// 平台检测
export const isNode = typeof process !== 'undefined'
&& process.versions != null
&& process.versions.node != null;
export const isBrowser = typeof window !== 'undefined'
&& typeof window.document !== 'undefined';
// 条件导出
export function getStorage() {
if (isNode) {
return require('./storage.node');
}
return require('./storage.browser');
}
# 四、包的维护与管理
# 4.1 版本更新策略
# 语义化版本的实践
根据改动类型选择正确的版本号:
Patch 版本(1.0.0 → 1.0.1)
- 修复 bug
- 性能优化
- 文档更新
- 内部重构(不影响 API)
npm version patch
Minor 版本(1.0.0 → 1.1.0)
- 新增功能
- 标记某功能为废弃(但保留)
- 内部大量重构
npm version minor
Major 版本(1.0.0 → 2.0.0)
- 删除已有 API
- 修改已有 API 的行为
- 需要用户修改代码的改动
npm version major
# 预发布版本策略
# 第一个 alpha 版本
npm version premajor --preid=alpha # 1.0.0 -> 2.0.0-alpha.0
npm version preminor --preid=alpha # 1.0.0 -> 1.1.0-alpha.0
npm version prepatch --preid=alpha # 1.0.0 -> 1.0.1-alpha.0
# 增加 prerelease 版本号
npm version prerelease # 1.0.0-alpha.0 -> 1.0.0-alpha.1
# 发布 beta 版本
npm version prerelease --preid=beta # 1.0.0-alpha.1 -> 1.0.0-beta.0
# 发布 rc (release candidate)
npm version prerelease --preid=rc # 1.0.0-beta.0 -> 1.0.0-rc.0
# 发布正式版本
npm version patch # 1.0.0-rc.0 -> 1.0.0
发布预发布版本到特定标签:
npm publish --tag alpha
npm publish --tag beta
npm publish --tag next
用户安装:
npm install my-package@alpha
npm install my-package@beta
npm install my-package@next
查看所有发布的版本:
npm view my-package versions
npm view my-package dist-tags
# 废弃旧版本
# 废弃某个版本
npm deprecate my-package@1.0.0 "This version has security issues, please upgrade to 1.0.1"
# 废弃某个范围的版本
npm deprecate my-package@"< 1.0.0" "Please upgrade to the latest version"
# 取消废弃
npm deprecate my-package@1.0.0 ""
# 4.2 npm scripts 最佳实践
# 命名约定
{
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"build": "tsc && vite build",
"build:dev": "vite build --mode development",
"build:prod": "vite build --mode production",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:e2e": "playwright test",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"format": "prettier --write \"src/**/*.ts\"",
"format:check": "prettier --check \"src/**/*.ts\"",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist",
"clean:all": "rm -rf dist node_modules"
}
}
# 组合脚本
使用 &&、|| 和 & 组合命令:
{
"scripts": {
"prebuild": "npm run clean",
"build": "npm run build:types && npm run build:js",
"build:types": "tsc --emitDeclarationOnly",
"build:js": "rollup -c",
"validate": "npm run lint && npm run type-check && npm run test",
"dev:all": "npm run dev:server & npm run dev:client",
"prepare": "npm run build || echo 'Build failed, using existing build'"
}
}
# 使用 npm-run-all
安装:
npm install --save-dev npm-run-all
{
"scripts": {
"clean": "rm -rf dist",
"build:types": "tsc --emitDeclarationOnly",
"build:js": "rollup -c",
"build": "npm-run-all clean --parallel build:*",
"lint:js": "eslint src",
"lint:css": "stylelint src",
"lint": "npm-run-all --parallel lint:*",
"test:unit": "jest",
"test:e2e": "playwright test",
"test": "npm-run-all --serial test:*"
}
}
# 环境变量
{
"scripts": {
"build": "NODE_ENV=production webpack",
"dev": "NODE_ENV=development webpack-dev-server"
}
}
跨平台支持,使用 cross-env:
npm install --save-dev cross-env
{
"scripts": {
"build": "cross-env NODE_ENV=production webpack",
"dev": "cross-env NODE_ENV=development webpack-dev-server"
}
}
# 4.3 发布前检查清单
# 自动化检查脚本
{
"scripts": {
"prepublishOnly": "npm run check",
"check": "npm-run-all --serial check:*",
"check:git": "node scripts/check-git.js",
"check:lint": "npm run lint",
"check:type": "npm run type-check",
"check:test": "npm run test",
"check:build": "npm run build",
"check:size": "size-limit"
}
}
scripts/check-git.js:
const { execSync } = require('child_process');
// 检查是否有未提交的改动
try {
const status = execSync('git status --porcelain').toString();
if (status) {
console.error('❌ Git working directory is not clean');
console.error('Please commit or stash your changes before publishing');
process.exit(1);
}
console.log('✓ Git working directory is clean');
} catch (error) {
console.error('❌ Git check failed');
process.exit(1);
}
// 检查是否在主分支
try {
const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
if (branch !== 'main' && branch !== 'master') {
console.warn(`⚠️ Warning: Publishing from branch "${branch}"`);
} else {
console.log(`✓ On ${branch} branch`);
}
} catch (error) {
console.error('❌ Branch check failed');
process.exit(1);
}
# 包大小检查
使用 size-limit (opens new window):
npm install --save-dev size-limit @size-limit/preset-small-lib
package.json:
{
"size-limit": [
{
"path": "dist/index.js",
"limit": "10 KB"
},
{
"path": "dist/index.mjs",
"limit": "10 KB"
}
]
}
# 发布检查清单
创建 PUBLISH_CHECKLIST.md:
# 发布检查清单
## 发布前
- [ ] 所有测试通过
- [ ] 文档已更新
- [ ] CHANGELOG.md 已更新
- [ ] package.json 版本号已更新
- [ ] 所有改动已提交到 git
- [ ] 在主分支上
## 发布
- [ ] `npm run build` 成功
- [ ] `npm pack` 检查发布文件
- [ ] `npm publish --dry-run` 测试
- [ ] `npm publish` 正式发布
## 发布后
- [ ] 推送代码到远程仓库
- [ ] 推送 git tag
- [ ] 创建 GitHub Release
- [ ] 更新文档网站
- [ ] 发布公告
# 4.4 维护 CHANGELOG
使用 conventional-changelog (opens new window):
npm install --save-dev conventional-changelog-cli
{
"scripts": {
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
}
}
CHANGELOG.md 示例:
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.2.0] - 2025-01-15
### Added
- New `formatDate` function for date formatting
- Support for custom locales
### Changed
- Improved performance of `parseDate` by 20%
### Deprecated
- `oldFormat` function will be removed in v2.0.0
### Fixed
- Fixed timezone handling in `convertDate`
## [1.1.0] - 2024-12-01
### Added
- Initial release
# 4.5 自动化发布
# 使用 GitHub Actions
.github/workflows/publish.yml:
name: Publish to NPM
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm test
- run: npm run build
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
body: See CHANGELOG.md for details
发布流程:
# 1. 更新版本并创建 tag
npm version patch # 或 minor/major
# 2. 推送代码和 tag
git push && git push --tags
# GitHub Actions 会自动发布
# 使用 np
np (opens new window) 是一个更好的 npm publish 工具:
npm install --global np
{
"scripts": {
"release": "np"
}
}
运行:
npm run release
np 会自动:
- 运行测试
- 检查 git 状态
- 更新版本号
- 发布到 npm
- 推送到 git
- 创建 GitHub Release
# 4.6 包的废弃与删除
# 废弃包
# 废弃整个包
npm deprecate my-package "This package is deprecated. Please use new-package instead."
# 删除包
注意:只能删除发布后 72 小时内的包!
# 删除特定版本
npm unpublish my-package@1.0.0
# 删除整个包(慎用!)
npm unpublish my-package --force
最佳实践:
- 不要删除已被广泛使用的包
- 优先使用
deprecate而不是unpublish - 如果必须删除,提前通知用户
# 五、npm 配置与工具
# 5.1 .npmrc 配置
项目级配置文件 .npmrc:
# 设置源
registry=https://registry.npmjs.org/
# 作用域包使用不同的源
@myorg:registry=https://npm.myorg.com/
# 保存依赖时使用精确版本
save-exact=true
# 使用 package-lock.json
package-lock=true
# 引擎严格模式
engine-strict=true
# 自动安装 peer dependencies (npm 7+)
legacy-peer-deps=false
# 5.2 镜像源管理
# 安装
npm install -g nrm
# 查看可用源
nrm ls
# 切换源
nrm use taobao
nrm use npm
# 测试速度
nrm test
# 添加自定义源
nrm add myorg https://npm.myorg.com/
或使用 npm 命令:
# 查看当前源
npm config get registry
# 设置源
npm config set registry https://registry.npmmirror.com
# 恢复官方源
npm config set registry https://registry.npmjs.org
# 5.3 私有 npm 仓库
# Verdaccio(推荐)
Verdaccio (opens new window) 是轻量级的私有 npm 仓库:
# 安装
npm install -g verdaccio
# 启动
verdaccio
配置 ~/.config/verdaccio/config.yaml:
storage: ./storage
auth:
htpasswd:
file: ./htpasswd
uplinks:
npmjs:
url: https://registry.npmjs.org/
packages:
'@myorg/*':
access: $authenticated
publish: $authenticated
'**':
access: $all
publish: $authenticated
proxy: npmjs
使用:
# 添加用户
npm adduser --registry http://localhost:4873
# 发布
npm publish --registry http://localhost:4873
# 安装
npm install @myorg/my-package --registry http://localhost:4873
或配置 .npmrc:
@myorg:registry=http://localhost:4873
# GitHub Packages
发布到 GitHub Packages:
# 登录
npm login --registry=https://npm.pkg.github.com
# 发布
npm publish --registry=https://npm.pkg.github.com
package.json:
{
"name": "@username/my-package",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}
# 5.4 依赖安全
# 检查漏洞
# 检查依赖中的安全漏洞
npm audit
# 自动修复
npm audit fix
# 查看详细报告
npm audit --json
# 使用 Snyk
Snyk (opens new window) 提供更全面的安全检查:
# 安装
npm install -g snyk
# 认证
snyk auth
# 测试
snyk test
# 持续监控
snyk monitor
# 六、最佳实践总结
# 6.1 项目初始化
# 使用 npm init 初始化新项目
npm init
# 或使用脚手架
npm create vite@latest
npm create next-app@latest
# 6.2 配置管理
- ✅ 统一团队 npm 配置:固化到
.npmrc文件中 - ✅ 统一运行环境:在
package.json中配置engines - ✅ 提交
package-lock.json:保证依赖版本一致
# 6.3 依赖管理
- ✅ 使用语义化版本:
^用于次版本更新,~用于补丁更新 - ✅ 区分依赖类型:正确使用 dependencies、devDependencies、peerDependencies
- ✅ 定期更新依赖:使用
npm outdated检查,npm update更新 - ✅ 检查安全漏洞:定期运行
npm audit
# 6.4 脚本管理
- ✅ 使用 npm scripts:管理应用相关脚本
- ✅ 使用 npx:运行本地安装的命令(npm >= 5.2)
- ✅ 命名规范:使用
:分隔命名空间,如build:dev、test:watch
# 6.5 发布管理
- ✅ 语义化版本:严格遵循 semver 规范
- ✅ 发布前检查:lint、test、build 全部通过
- ✅ 控制发布文件:使用
files字段明确指定 - ✅ 使用 prepublishOnly:自动化发布前检查
- ✅ 维护 CHANGELOG:记录每个版本的改动
# 6.6 文档
- ✅ 完善的 README:安装、使用、示例、API 文档
- ✅ TypeScript 类型定义:提供
.d.ts文件 - ✅ 示例代码:提供可运行的示例
- ✅ 变更日志:维护 CHANGELOG.md
# 6.7 质量保证
- ✅ 单元测试:覆盖核心功能
- ✅ 类型检查:使用 TypeScript 或 JSDoc
- ✅ 代码规范:使用 ESLint、Prettier
- ✅ 持续集成:使用 GitHub Actions 自动化测试和发布
# 七、附录:package.json 字段完全解读
# 7.1 描述信息
# name - 包名
项目的名称,如果要发布包,这是必需字段。
规则:
- 长度:≤ 214 字符
- 命名:
- 必须全部小写
- 可以包含连字符
-和下划线_ - 不能以
.或_开头 - 不能包含大写字母
- 不能包含非 URL 安全字符
- 不能与 Node.js 核心模块重名
- 在 npm registry 中必须唯一
作用域包:
{
"name": "@myorg/my-package"
}
# version - 版本号
遵循 Semantic Versioning (opens new window) 规范,格式为 X.Y.Z:
- X(Major):主版本号,不兼容的 API 修改
- Y(Minor):次版本号,向下兼容的功能新增
- Z(Patch):修订号,向下兼容的问题修正
预发布版本:
1.0.0-alpha.0:alpha 版本1.0.0-beta.0:beta 版本1.0.0-rc.0:release candidate
{
"version": "1.2.3"
}
# description - 描述
简短描述包的功能,显示在 npm 搜索结果中:
{
"description": "A utility library for date formatting"
}
# keywords - 关键词
关键词数组,帮助用户在 npm 上找到你的包:
{
"keywords": [
"date",
"time",
"format",
"utility"
]
}
# homepage - 主页
项目主页 URL:
{
"homepage": "https://github.com/username/project#readme"
}
# repository - 代码仓库
指定代码仓库的位置:
{
"repository": {
"type": "git",
"url": "https://github.com/username/project.git"
}
}
简写形式:
{
"repository": "github:username/project"
}
# bugs - 问题追踪
用户提交 bug 的地方:
{
"bugs": {
"url": "https://github.com/username/project/issues",
"email": "bugs@example.com"
}
}
或简写:
{
"bugs": "https://github.com/username/project/issues"
}
# license - 许可证
指定包的许可证:
{
"license": "MIT"
}
多个许可证:
{
"license": "(MIT OR Apache-2.0)"
}
常见许可证:
MIT:宽松许可证Apache-2.0:Apache 许可证GPL-3.0:GNU 许可证ISC:类似 MITBSD-3-Clause:BSD 许可证
# author - 作者
包的作者信息:
{
"author": "Your Name <you@example.com> (https://yoursite.com)"
}
或对象形式:
{
"author": {
"name": "Your Name",
"email": "you@example.com",
"url": "https://yoursite.com"
}
}
# contributors - 贡献者
贡献者列表:
{
"contributors": [
"Name1 <email1@example.com>",
"Name2 <email2@example.com>"
]
}
# funding - 资助
资助信息,显示在 npm install 后:
{
"funding": {
"type": "github",
"url": "https://github.com/sponsors/username"
}
}
多个资助渠道:
{
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/username"
},
{
"type": "patreon",
"url": "https://patreon.com/username"
}
]
}
# 7.2 文件配置
# files - 发布文件
指定发布到 npm 的文件列表(白名单):
{
"files": [
"dist",
"lib",
"bin",
"README.md",
"LICENSE"
]
}
始终包含的文件:
package.jsonREADME.md/READMELICENSE/LICENCEmain字段指定的文件
始终忽略的文件:
.gitnode_modules.npmrcpackage-lock.json(可用publishConfig.packages包含)
# main - 主入口
CommonJS 格式的主入口文件:
{
"main": "dist/index.js"
}
用户使用 require('your-package') 时加载此文件。
# module - ES Module 入口
ES Module 格式的入口文件:
{
"module": "dist/index.esm.js"
}
打包工具会优先使用此字段进行 Tree Shaking。
# browser - 浏览器入口
浏览器环境的入口文件:
{
"browser": "dist/browser.js"
}
或文件映射:
{
"browser": {
"./server.js": "./browser.js",
"fs": false
}
}
# exports - 现代入口配置
最现代的入口配置方式,优先级最高:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"default": "./dist/index.js"
},
"./utils": "./dist/utils.js",
"./package.json": "./package.json"
}
}
特性:
- 支持条件导出
- 支持子路径导出
- 封装性:未定义的路径无法访问
# type - 模块类型
指定 .js 文件的模块类型:
{
"type": "module"
}
"module":.js文件为 ES Module"commonjs":.js文件为 CommonJS(默认)
注意:
- 设置为
"module"后,需要用.cjs扩展名表示 CommonJS - 设置为
"commonjs"后,需要用.mjs扩展名表示 ES Module
# types / typings - 类型定义
TypeScript 类型定义文件:
{
"types": "dist/index.d.ts"
}
或:
{
"typings": "dist/index.d.ts"
}
# man - 手册页
Linux/Unix man 命令的手册页:
{
"man": "./man/doc.1"
}
或多个:
{
"man": [
"./man/doc.1",
"./man/doc.2"
]
}
# directories - 目录结构
描述包的目录结构(较少使用):
{
"directories": {
"lib": "src",
"bin": "bin",
"man": "man",
"doc": "docs",
"example": "examples",
"test": "test"
}
}
# 7.3 脚本配置
# scripts - 脚本命令
定义可运行的脚本命令:
{
"scripts": {
"start": "node server.js",
"dev": "vite",
"build": "vite build",
"test": "jest",
"lint": "eslint src"
}
}
生命周期钩子:
| 钩子 | 执行时机 |
|---|---|
preinstall | install 之前 |
install / postinstall | install 之后 |
prepack | npm pack 之前 |
postpack | npm pack 之后 |
prepublish | npm publish 之前(已废弃) |
prepublishOnly | npm publish 之前(推荐) |
preversion | 修改版本号之前 |
version | 修改版本号之后,提交之前 |
postversion | 提交之后 |
自定义生命周期:
pre<script>:在脚本之前执行post<script>:在脚本之后执行
{
"scripts": {
"prebuild": "rm -rf dist",
"build": "tsc",
"postbuild": "echo 'Build complete!'"
}
}
# config - 配置参数
为脚本提供配置参数:
{
"config": {
"port": "8080"
},
"scripts": {
"start": "node server.js $npm_package_config_port"
}
}
在代码中访问:
console.log(process.env.npm_package_config_port); // "8080"
用户可以覆盖:
npm config set my-package:port 3000
# bin - 可执行文件
指定可执行脚本:
{
"bin": {
"mycli": "./bin/cli.js"
}
}
包名和命令名相同时可简写:
{
"name": "mycli",
"bin": "./bin/cli.js"
}
注意:
- 脚本文件必须以
#!/usr/bin/env node开头 - 文件需要有执行权限
# 7.4 依赖配置
# dependencies - 生产依赖
生产环境需要的依赖:
{
"dependencies": {
"react": "^18.2.0",
"axios": "~1.6.0",
"lodash": "4.17.21"
}
}
版本规范:
^1.2.3:兼容 1.x.x(>=1.2.3 <2.0.0)~1.2.3:兼容 1.2.x(>=1.2.3 <1.3.0)1.2.3:精确版本>1.2.3、>=1.2.3、<1.2.3、<=1.2.3:比较1.2.3 - 2.3.4:范围(等同于>=1.2.3 <=2.3.4)*或x:任意版本latest:最新版本http://...:URLgit://...:Git URLgithub:user/repo:GitHub 简写file:../path:本地路径
# devDependencies - 开发依赖
开发环境需要的依赖:
{
"devDependencies": {
"typescript": "^5.3.0",
"eslint": "^8.56.0",
"jest": "^29.7.0",
"@types/node": "^20.10.0"
}
}
安装方式:
npm install --save-dev typescript
# 或
npm install -D typescript
# peerDependencies - 对等依赖
声明包需要的宿主环境依赖:
{
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
}
使用场景:
- 插件依赖宿主应用
- 库依赖特定框架版本
npm 版本行为差异:
- npm 1-2:自动安装 peerDependencies
- npm 3-6:只警告,不自动安装
- npm 7+:自动安装 peerDependencies
# peerDependenciesMeta - 对等依赖元数据
标记可选的 peer dependencies:
{
"peerDependencies": {
"react": ">=16.8.0",
"vue": ">=3.0.0"
},
"peerDependenciesMeta": {
"vue": {
"optional": true
}
}
}
表示 Vue 是可选的对等依赖。
# optionalDependencies - 可选依赖
可选依赖,安装失败不影响包的使用:
{
"optionalDependencies": {
"fsevents": "^2.3.2"
}
}
注意:
- 会覆盖
dependencies中的同名依赖 - 代码中需要做降级处理
try {
const fsevents = require('fsevents');
} catch (err) {
// 降级方案
}
# bundledDependencies / bundleDependencies - 打包依赖
发布时打包进 tarball 的依赖:
{
"bundledDependencies": [
"private-package",
"custom-build"
]
}
使用场景:
- 私有包
- 需要特殊构建的包
- 离线安装
# overrides - 依赖覆盖(npm 8.3+)
强制覆盖依赖树中的版本:
{
"overrides": {
"lodash": "^4.17.21",
"axios": {
".": "1.6.0",
"follow-redirects": "^1.15.4"
}
}
}
类似功能:
- Yarn:
resolutions - pnpm:
pnpm.overrides
# 7.5 发布配置
# private - 私有包
防止意外发布:
{
"private": true
}
设置后 npm publish 会失败。
# publishConfig - 发布配置
发布时使用的配置:
{
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/",
"tag": "latest"
}
}
常用字段:
access:public或restrictedregistry:发布到的 registrytag:发布标签directory:发布的目录
# 7.6 系统配置
# engines - 运行环境
指定运行环境的版本要求:
{
"engines": {
"node": ">=18.0.0",
"npm": ">=9.0.0",
"yarn": ">=1.22.0"
}
}
注意:
- 默认只是警告
- 要强制执行,需要设置
.npmrc:engine-strict=true
# os - 操作系统
指定支持的操作系统:
{
"os": ["darwin", "linux"]
}
黑名单:
{
"os": ["!win32"]
}
# cpu - CPU 架构
指定支持的 CPU 架构:
{
"cpu": ["x64", "arm64"]
}
黑名单:
{
"cpu": ["!arm", "!mips"]
}
# 7.7 工作空间
# workspaces - Monorepo
定义工作空间,用于 monorepo 管理:
{
"workspaces": [
"packages/*",
"apps/*"
]
}
或排除某些目录:
{
"workspaces": {
"packages": [
"packages/*"
],
"nohoist": [
"**/react-native",
"**/react-native/**"
]
}
}
特性:
- 共享 node_modules
- 统一安装依赖
- 支持工作空间间的相互引用
- 批量执行脚本
使用示例:
# 为特定工作空间添加依赖
npm install lodash --workspace=packages/utils
# 在所有工作空间运行脚本
npm run build --workspaces
# 发布所有工作空间
npm publish --workspaces --access public
# 7.8 第三方配置
这些配置用于特定工具,不是 npm 官方字段。
# sideEffects - 副作用(Webpack)
标记模块是否有副作用,用于 Tree Shaking:
{
"sideEffects": false
}
标记特定文件有副作用:
{
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfills.js"
]
}
# unpkg / jsdelivr - CDN
指定 CDN 入口文件:
{
"unpkg": "dist/index.umd.js",
"jsdelivr": "dist/index.umd.js"
}
用户可以通过 CDN 访问:
<script src="https://unpkg.com/your-package"></script>
<script src="https://cdn.jsdelivr.net/npm/your-package"></script>
# browserslist - 浏览器兼容
指定浏览器兼容范围(用于 Babel、Autoprefixer 等):
{
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}
或分环境:
{
"browserslist": {
"production": [
"> 1%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
# lint-staged - Git Hooks
配合 husky 使用,对暂存文件执行命令:
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md}": [
"prettier --write"
]
}
}
# eslintConfig - ESLint
内联 ESLint 配置(不推荐,建议使用独立文件):
{
"eslintConfig": {
"extends": ["eslint:recommended"],
"env": {
"node": true,
"es6": true
}
}
}
# prettier - Prettier
内联 Prettier 配置(不推荐,建议使用独立文件):
{
"prettier": {
"semi": false,
"singleQuote": true,
"tabWidth": 2
}
}
# jest - Jest
内联 Jest 配置(不推荐,建议使用独立文件):
{
"jest": {
"testEnvironment": "node",
"coverageDirectory": "coverage"
}
}
# 参考资料
# 官方文档
- NPM package.json v11 (opens new window)
- NPM CLI Documentation (opens new window)
- Semantic Versioning (opens new window)
- Node.js Documentation (opens new window)
# 深入学习
- 2018 年了,你还是只会 npm install 吗? (opens new window)
- How to Publish an npm Package with TypeScript (Step-by-Step) (opens new window)
- package.json 配置完全解读 (opens new window)
- package.json 配置详解 (opens new window)
- 从 package.json 来聊聊如何管理一款优秀的 Npm 包 (opens new window)
# 工具与生态
- np - Better npm publish (opens new window)
- Verdaccio - Private npm registry (opens new window)
- size-limit - Calculate the real cost to run your JS (opens new window)
- conventional-changelog - Generate changelogs (opens new window)
# 总结
本文系统地介绍了 npm 包的发布与管理,从快速入门到高级实践,涵盖了:
- 快速开始:最小化配置,发布第一个包
- 进阶配置:版本管理、文件管理、入口配置、依赖管理
- 最佳实践:不同类型包的完整案例
- 维护管理:版本更新、自动化发布、质量保证
- 完整参考:package.json 所有字段详解
希望这份指南能够帮助你更好地理解和使用 npm,创建高质量的 npm 包!
如果你觉得本文有帮助,欢迎分享给更多的开发者。如有疑问或建议,欢迎在评论区讨论。