模块化与package.json
模块化
CommonJS 模块化规范
CommonJS 是 Node.js 采用的模块化规范,主要用于服务端 JavaScript 开发。它使用同步加载方式,适合服务端环境。
CommonJS 基本用法
CommonJS 使用 require()
函数导入模块,使用 module.exports
或 exports
导出模块。
导出模块:
javascript
// math.js - 导出单个函数
module.exports = function add(a, b) {
return a + b;
};
// utils.js - 导出多个函数
exports.formatDate = function(date) {
return date.toISOString().split('T')[0];
};
exports.generateId = function() {
return Math.random().toString(36).substring(2, 15);
};
// calculator.js - 导出对象
module.exports = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => b !== 0 ? a / b : null
};
导入模块:
javascript
// 导入单个函数
const add = require('./math');
console.log(add(2, 3)); // 5
// 导入多个函数
const { formatDate, generateId } = require('./utils');
console.log(formatDate(new Date())); // 2024-01-01
// 导入整个模块
const calculator = require('./calculator');
console.log(calculator.add(10, 5)); // 15
// 导入核心模块
const fs = require('fs');
const path = require('path');
示例
完整的模块化项目示例:
javascript
// config/database.js
module.exports = {
development: {
host: 'localhost',
port: 5432,
database: 'myapp_dev'
},
production: {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME
}
};
// services/userService.js
const database = require('../config/database');
class UserService {
constructor() {
this.users = [];
}
addUser(user) {
this.users.push({ ...user, id: Date.now() });
return this.users[this.users.length - 1];
}
getUserById(id) {
return this.users.find(user => user.id === id);
}
getAllUsers() {
return this.users;
}
}
module.exports = new UserService();
// app.js
const userService = require('./services/userService');
const newUser = userService.addUser({
name: 'Alice',
email: 'alice@example.com'
});
console.log('Created user:', newUser);
console.log('All users:', userService.getAllUsers());
CommonJS 特点
优势:
- 同步加载: 模块加载是同步的,代码执行顺序可预期
- 简单易用: 语法简洁,学习成本低
- 缓存机制: 模块只会被加载一次,后续调用直接从缓存获取
- 动态加载: 支持条件加载和动态路径
劣势:
- 阻塞加载: 同步加载在浏览器环境下会阻塞页面渲染
- 无法静态分析: 难以进行 Tree Shaking 优化
- 循环依赖: 可能导致循环依赖问题
模块加载流程:
ECMAScript Modules (ESM) 规范
ECMAScript Modules (ESM) 是 JavaScript 官方的模块化标准,在现代浏览器和 Node.js 中都得到支持。ESM 使用静态结构,支持异步加载和 Tree Shaking 优化。
ECMAScript Modules 基本用法
ESM 使用 import
和 export
语句来处理模块的导入和导出。
导出模块:
javascript
// math.mjs - 命名导出
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export const PI = 3.14159;
// utils.mjs - 默认导出 + 命名导出
export default function formatDate(date) {
return date.toISOString().split('T')[0];
}
export function generateId() {
return Math.random().toString(36).substring(2, 15);
}
export function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
// calculator.mjs - 统一导出
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
return b !== 0 ? a / b : null;
}
class Calculator {
constructor() {
this.history = [];
}
calculate(operation, a, b) {
let result;
switch(operation) {
case 'add': result = a + b; break;
case 'subtract': result = a - b; break;
case 'multiply': result = multiply(a, b); break;
case 'divide': result = divide(a, b); break;
default: throw new Error('Unknown operation');
}
this.history.push({ operation, a, b, result });
return result;
}
}
export { multiply, divide, Calculator as default };
导入模块:
javascript
// 导入命名导出
import { add, subtract, PI } from './math.mjs';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159
// 导入默认导出
import formatDate from './utils.mjs';
console.log(formatDate(new Date())); // 2024-01-01
// 导入默认导出和命名导出
import formatDate, { generateId, debounce } from './utils.mjs';
// 导入所有导出
import * as mathUtils from './math.mjs';
console.log(mathUtils.add(5, 3)); // 8
// 重命名导入
import { add as sum, subtract as minus } from './math.mjs';
console.log(sum(10, 5)); // 15
// 动态导入
async function loadCalculator() {
const { default: Calculator } = await import('./calculator.mjs');
const calc = new Calculator();
return calc.calculate('add', 10, 20);
}
ECMAScript Modules 示例
完整的 ESM 项目示例:
javascript
// config/environment.mjs
export const config = {
development: {
apiUrl: 'http://localhost:3000/api',
debug: true
},
production: {
apiUrl: 'https://api.example.com',
debug: false
}
};
export default config[process.env.NODE_ENV || 'development'];
// services/apiClient.mjs
import config from '../config/environment.mjs';
export class ApiClient {
constructor() {
this.baseUrl = config.apiUrl;
this.headers = {
'Content-Type': 'application/json'
};
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const response = await fetch(url, {
...options,
headers: { ...this.headers, ...options.headers }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
get(endpoint) {
return this.request(endpoint);
}
post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
}
export default new ApiClient();
// models/User.mjs
import apiClient from '../services/apiClient.mjs';
export class User {
constructor(userData) {
this.id = userData.id;
this.name = userData.name;
this.email = userData.email;
this.createdAt = new Date(userData.createdAt);
}
static async findById(id) {
const userData = await apiClient.get(`/users/${id}`);
return new User(userData);
}
static async findAll() {
const usersData = await apiClient.get('/users');
return usersData.map(userData => new User(userData));
}
async save() {
if (this.id) {
const updated = await apiClient.put(`/users/${this.id}`, this.toJSON());
return new User(updated);
} else {
const created = await apiClient.post('/users', this.toJSON());
return new User(created);
}
}
toJSON() {
return {
name: this.name,
email: this.email
};
}
}
// app.mjs
import { User } from './models/User.mjs';
import { debounce } from './utils.mjs';
async function main() {
try {
const users = await User.findAll();
console.log('All users:', users);
const newUser = new User({
name: 'Bob',
email: 'bob@example.com'
});
const savedUser = await newUser.save();
console.log('Created user:', savedUser);
} catch (error) {
console.error('Error:', error.message);
}
}
main();
ESM 特点
优势:
- 静态分析: 编译时即可确定模块依赖关系,支持 Tree Shaking
- 异步加载: 支持动态导入,不阻塞主线程
- 标准化: ES6 官方标准,浏览器和 Node.js 原生支持
- 严格模式: 模块代码自动运行在严格模式下
- 顶级作用域: 模块顶级作用域不是全局作用域
劣势:
- 兼容性: 老版本 Node.js 和浏览器需要 Polyfill
- 学习成本: 相比 CommonJS 语法稍复杂
- 文件扩展名: 需要明确文件扩展名或配置
ESM 加载流程:
文件后缀与模块加载规则
在 Node.js 中,模块类型的识别主要通过文件扩展名和 package.json 配置来确定。理解这些规则对于模块化开发至关重要。
CommonJS 文件后缀
默认扩展名:
.js
- 默认作为 CommonJS 模块.cjs
- 明确指定为 CommonJS 模块.node
- 原生 C++ 插件模块
javascript
// math.js (CommonJS)
module.exports = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
// utils.cjs (明确指定 CommonJS)
exports.formatNumber = function(num) {
return num.toLocaleString();
};
// 引用方式
const math = require('./math'); // 可以省略 .js
const utils = require('./utils.cjs'); // 必须明确 .cjs
ECMAScript 模块的文件后缀
ESM 专用扩展名:
.mjs
- 明确指定为 ESM 模块.js
- 当 package.json 中"type": "module"
时作为 ESM
javascript
// math.mjs (ESM)
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// 当 package.json 包含 "type": "module" 时
// utils.js 会被识别为 ESM
export default function formatNumber(num) {
return num.toLocaleString();
}
export const constants = {
PI: 3.14159,
E: 2.71828
};
如何区分 CommonJS 和 ESM
Node.js 使用以下规则确定模块类型:
模块类型判断流程:
package.json 配置示例:
javascript
// package.json - ESM 项目
{
"name": "my-esm-project",
"version": "1.0.0",
"type": "module",
"main": "index.js",
"exports": {
".": "./index.js",
"./utils": "./utils.js"
}
}
// package.json - CommonJS 项目
{
"name": "my-cjs-project",
"version": "1.0.0",
"type": "commonjs", // 可选,默认就是 CommonJS
"main": "index.js"
}
// package.json - 混合项目
{
"name": "mixed-project",
"version": "1.0.0",
"type": "module",
"exports": {
".": {
"import": "./index.mjs",
"require": "./index.cjs"
},
"./utils": {
"import": "./utils.mjs",
"require": "./utils.cjs"
}
}
}
实际项目结构示例:
lua
project/
├── package.json # "type": "module"
├── index.js # ESM (因为 package.json 设置)
├── config.mjs # ESM (明确扩展名)
├── legacy.cjs # CommonJS (明确扩展名)
└── lib/
├── package.json # "type": "commonjs"
├── utils.js # CommonJS (子目录配置覆盖)
└── modern.mjs # ESM (明确扩展名)
模块解析优先级:
- 文件扩展名 -
.mjs
和.cjs
具有最高优先级 - package.json type 字段 - 决定
.js
文件的解析方式 - 默认行为 - 未配置时
.js
默认为 CommonJS
注意事项:
- ESM 中必须明确指定文件扩展名:
import './module.js'
- CommonJS 中可以省略
.js
扩展名:require('./module')
- 混合使用时建议使用明确的
.mjs
和.cjs
扩展名
require 和 import 互操作性
CommonJS 和 ESM 之间的互操作性是现代 JavaScript 开发中的重要话题。虽然两种模块系统设计理念不同,但 Node.js 提供了一定的互操作能力。
从 CommonJS 中引入 ESM
**注意:**CommonJS 不能直接使用 require()
同步加载 ESM 模块,必须使用异步的 import()
函数。
javascript
// esm-module.mjs (ESM模块)
export const PI = 3.14159;
export default function calculate(radius) {
return PI * radius * radius;
}
export class Calculator {
add(a, b) { return a + b; }
multiply(a, b) { return a * b; }
}
// commonjs-consumer.cjs (CommonJS 消费者)
// ❌ 错误方式 - 不能直接require ESM
// const esmModule = require('./esm-module.mjs'); // 报错!
// ✅ 正确方式 - 使用动态 import()
async function loadESMModule() {
try {
// 导入默认导出和命名导出
const { default: calculate, PI, Calculator } = await import('./esm-module.mjs');
console.log('PI:', PI); // 3.14159
console.log('Area:', calculate(5)); // 78.5397...
const calc = new Calculator();
console.log('Add:', calc.add(10, 20)); // 30
return { calculate, PI, Calculator };
} catch (error) {
console.error('Failed to load ESM module:', error);
}
}
// 在异步上下文中使用
loadESMModule().then(esm => {
if (esm) {
console.log('ESM module loaded successfully');
}
});
// 或者在 async 函数中使用
async function main() {
const esm = await loadESMModule();
// 使用导入的模块...
}
main().catch(console.error);
// 实际项目示例
class DatabaseService {
constructor() {
this.ormModule = null;
}
async initialize() {
// 动态加载现代ORM模块(通常是ESM)
const { default: ORM, createConnection } = await import('modern-orm');
this.ormModule = ORM;
this.connection = await createConnection({
host: 'localhost',
database: 'myapp'
});
console.log('Database initialized with modern ORM');
}
async query(sql) {
if (!this.ormModule) {
await this.initialize();
}
return this.connection.query(sql);
}
}
module.exports = DatabaseService;
从 ESM 中引入 CommonJS
ESM 可以直接导入 CommonJS 模块 ,Node.js 会自动包装 CommonJS 的 module.exports
。
javascript
// commonjs-module.cjs (CommonJS模块)
const fs = require('fs');
const path = require('path');
function readConfig(configPath) {
const fullPath = path.resolve(configPath);
const content = fs.readFileSync(fullPath, 'utf8');
return JSON.parse(content);
}
class FileManager {
constructor(basePath) {
this.basePath = basePath;
}
readFile(filename) {
return fs.readFileSync(path.join(this.basePath, filename), 'utf8');
}
writeFile(filename, content) {
fs.writeFileSync(path.join(this.basePath, filename), content);
}
}
// 导出方式1:直接导出函数
module.exports = readConfig;
// 导出方式2:导出对象
module.exports = {
readConfig,
FileManager
};
// 导出方式3:使用 exports
exports.readConfig = readConfig;
exports.FileManager = FileManager;
// esm-consumer.mjs (ESM 消费者)
// ✅ 直接导入 CommonJS 默认导出
import readConfig from './commonjs-module.cjs';
import { readConfig as configReader, FileManager } from './commonjs-module.cjs';
// 使用导入的功能
const config = readConfig('./config.json');
console.log('Config:', config);
const fileManager = new FileManager('./data');
const content = fileManager.readFile('example.txt');
console.log('File content:', content);
// 导入 Node.js 内置 CommonJS 模块
import fs from 'fs';
import path from 'path';
import { readFileSync } from 'fs';
import { resolve } from 'path';
console.log('Current directory:', process.cwd());
// 实际项目示例:现代ESM项目使用传统CommonJS工具
import express from 'express'; // CommonJS 模块
import morgan from 'morgan'; // CommonJS 模块
import helmet from 'helmet'; // CommonJS 模块
// 现代 ESM 配置
import { config } from './config.mjs';
import { logger } from './utils/logger.mjs';
const app = express();
// 配置中间件
app.use(helmet());
app.use(morgan('combined'));
app.use(express.json());
app.get('/', (req, res) => {
res.json({
message: 'ESM app using CommonJS dependencies',
config: config.app.name
});
});
const port = config.server.port || 3000;
app.listen(port, () => {
logger.info(`Server running on port ${port}`);
});
互操作性对照表:
场景 | CommonJS → ESM | ESM → CommonJS |
---|---|---|
同步导入 | ❌ 不支持 (require() 不能加载 ESM) |
✅ 支持 (import 语句) |
异步导入 | ✅ 支持 (await import() ) |
✅ 支持 (await import() ) |
默认导出 | 需使用 { default } 解构 |
直接可用 |
命名导出 | 正常解构 | 正常解构 |
性能 | 异步加载有开销 | 静态优化 |
最佳实践:
javascript
// 创建适配器函数处理异步导入
// adapter.cjs
async function createESMAdapter(modulePath) {
const esm = await import(modulePath);
return {
...esm,
default: esm.default
};
}
module.exports = { createESMAdapter };
// 使用示例
async function useModernLibrary() {
const { createESMAdapter } = require('./adapter.cjs');
const modernLib = await createESMAdapter('modern-library');
return modernLib.default();
}
选择 CommonJS 还是 ESM?
选择合适的模块化方案对项目的长期维护和性能至关重要。以下是详细的选择指南和决策流程。
选择决策流程
CommonJS 适用场景
推荐使用 CommonJS 的情况:
javascript
// 1. 传统 Node.js 项目维护
// legacy-server.js
const express = require('express');
const morgan = require('morgan');
const config = require('./config');
const app = express();
app.use(morgan('combined'));
// 传统的中间件和路由配置
app.get('/api/users', (req, res) => {
// 业务逻辑
});
module.exports = app;
// 2. 需要条件加载的场景
const isDevelopment = process.env.NODE_ENV === 'development';
let dbConnection;
if (isDevelopment) {
dbConnection = require('./dev-db');
} else {
dbConnection = require('./prod-db');
}
// 3. 简单脚本和工具
// build-script.js
const fs = require('fs');
const path = require('path');
function buildProject() {
const srcDir = path.join(__dirname, 'src');
const distDir = path.join(__dirname, 'dist');
// 构建逻辑
console.log('Building project...');
}
if (require.main === module) {
buildProject();
}
module.exports = buildProject;
CommonJS 优势场景:
- 老版本兼容: Node.js < 14 或需要兼容老版本
- 简单项目: 小型工具、脚本、传统后端服务
- 团队熟悉度: 团队对 CommonJS 更熟悉
- 第三方依赖: 大量依赖仍然是 CommonJS
- 动态加载: 需要大量条件加载和动态路径
ESM 适用场景
推荐使用 ESM 的情况:
javascript
// 1. 现代前端项目
// main.js
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
// 路由配置
]
});
const app = createApp(App);
app.use(router);
app.mount('#app');
// 2. 现代 Node.js 服务
// server.mjs
import express from 'express';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { readFileSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const app = express();
// 静态资源服务
app.use(express.static(join(__dirname, 'public')));
// API 路由
app.get('/api/config', (req, res) => {
const config = JSON.parse(
readFileSync(join(__dirname, 'config.json'), 'utf8')
);
res.json(config);
});
export default app;
// 3. 库和组件开发
// utils/validator.mjs
export class Validator {
static isEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
static isPhone(phone) {
const regex = /^\+?[\d\s-()]+$/;
return regex.test(phone);
}
}
export function sanitizeInput(input) {
return input.toString().trim();
}
export default {
Validator,
sanitizeInput
};
ESM 优势场景:
- 现代项目: 新项目,使用最新技术栈
- 前端开发: 浏览器原生支持,打包工具优化
- Tree Shaking: 需要代码优化和死代码消除
- TypeScript: 更好的类型支持和静态分析
- 微前端: 模块联邦等现代架构
混合使用策略
在实际项目中,可能需要同时使用两种模块系统:
javascript
// package.json 配置
{
"name": "hybrid-project",
"version": "1.0.0",
"type": "module",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./utils": {
"import": "./dist/utils.mjs",
"require": "./dist/utils.cjs"
}
},
"scripts": {
"build": "rollup -c rollup.config.js",
"build:cjs": "babel src --out-dir dist/cjs --plugins @babel/plugin-transform-modules-commonjs",
"build:esm": "babel src --out-dir dist/esm"
}
}
// rollup.config.js - 双格式构建
export default [
// ESM 版本
{
input: 'src/index.js',
output: {
file: 'dist/index.mjs',
format: 'esm'
}
},
// CommonJS 版本
{
input: 'src/index.js',
output: {
file: 'dist/index.cjs',
format: 'cjs'
}
}
];
迁移策略
从 CommonJS 迁移到 ESM:
javascript
// 迁移示例
// 原 CommonJS 代码
const path = require('path');
const fs = require('fs');
const configPath = path.join(__dirname, 'config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
module.exports = config;
// 迁移后 ESM 代码
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { readFileSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const configPath = join(__dirname, 'config.json');
const config = JSON.parse(readFileSync(configPath, 'utf8'));
export default config;
最终建议
新项目推荐:
- 前端项目: ESM + 现代构建工具
- Node.js 服务: ESM (Node.js >= 14)
- 库开发: 双格式发布 (ESM + CommonJS)
- 工具脚本: 根据复杂度选择
现有项目:
- 稳定项目: 保持 CommonJS,避免不必要风险
- 活跃开发: 逐步迁移到 ESM
- 混合依赖: 使用工具链处理兼容性
团队考虑:
- 团队技能水平和学习成本
- 项目维护周期和稳定性要求
- 第三方依赖的模块系统偏好
package.json中全部字段配置含义
package.json 是 Node.js 项目的核心配置文件,定义了项目的元数据、依赖关系、脚本命令等信息。以下是所有重要字段的详细说明。
基本信息字段
name (必需)
项目名称,必须是小写字母、数字、连字符或下划线的组合。
javascript
{
"name": "my-awesome-project",
"name": "@myorg/my-project" // 作用域包名
}
version (必需)
项目版本,必须遵循语义化版本规范 (SemVer)。
javascript
{
"version": "1.2.3", // 主版本.次版本.修订版本
"version": "1.0.0-alpha.1" // 预发布版本
}
description
项目的简短描述,显示在 npm 搜索结果中。
javascript
{
"description": "A modern web application framework for Node.js"
}
keywords
关键词数组,帮助用户在 npm 上发现你的包。
javascript
{
"keywords": ["web", "framework", "node", "typescript", "api"]
}
author / contributors
作者和贡献者信息。
javascript
{
"author": "John Doe <john@example.com> (https://johndoe.com)",
"author": {
"name": "John Doe",
"email": "john@example.com",
"url": "https://johndoe.com"
},
"contributors": [
"Jane Smith <jane@example.com>",
{
"name": "Bob Wilson",
"email": "bob@example.com"
}
]
}
license
项目许可证。
javascript
{
"license": "MIT",
"license": "(MIT OR Apache-2.0)",
"license": {
"type": "MIT",
"url": "https://opensource.org/licenses/MIT"
}
}
入口点和模块配置
main
CommonJS 模块的主入口点。
javascript
{
"main": "index.js", // 默认入口
"main": "dist/index.js", // 构建后的入口
"main": "lib/index.cjs" // CommonJS 入口
}
module
ESM 模块的主入口点(非官方标准,但广泛支持)。
javascript
{
"module": "index.mjs", // ESM 入口
"module": "dist/index.esm.js" // 构建后的 ESM 入口
}
exports
现代的入口点配置,支持条件导出。
javascript
{
"exports": {
".": {
"import": "./dist/index.mjs", // ESM 导入
"require": "./dist/index.cjs", // CommonJS 导入
"types": "./dist/index.d.ts" // TypeScript 类型
},
"./utils": {
"import": "./dist/utils.mjs",
"require": "./dist/utils.cjs"
},
"./package.json": "./package.json"
},
"exports": "./index.js", // 简单导出
"exports": {
".": "./index.js",
"./feature": "./feature.js"
}
}
type
指定模块系统类型。
javascript
{
"type": "module", // 将 .js 文件视为 ESM
"type": "commonjs" // 将 .js 文件视为 CommonJS (默认)
}
bin
可执行文件配置。
javascript
{
"bin": "cli.js", // 单个命令
"bin": {
"my-cli": "./bin/cli.js", // 多个命令
"my-tool": "./bin/tool.js"
}
}
依赖管理
dependencies
生产环境依赖。
javascript
{
"dependencies": {
"express": "^4.18.0", // 兼容更新
"lodash": "~4.17.21", // 补丁更新
"react": "18.2.0", // 精确版本
"vue": ">=3.0.0 <4.0.0", // 版本范围
"axios": "latest", // 最新版本 (不推荐)
"local-package": "file:../local" // 本地依赖
}
}
devDependencies
开发环境依赖。
javascript
{
"devDependencies": {
"@types/node": "^18.0.0", // TypeScript 类型
"typescript": "^4.8.0", // 开发工具
"jest": "^28.0.0", // 测试框架
"eslint": "^8.0.0", // 代码检查
"webpack": "^5.70.0" // 构建工具
}
}
peerDependencies
对等依赖,要求使用者提供。
javascript
{
"peerDependencies": {
"react": ">=16.8.0", // React 插件
"vue": "^3.0.0" // Vue 插件
},
"peerDependenciesMeta": {
"react": {
"optional": true // 可选对等依赖
}
}
}
optionalDependencies
可选依赖,安装失败不会导致整个安装失败。
javascript
{
"optionalDependencies": {
"sharp": "^0.30.0", // 图像处理 (原生依赖)
"node-sass": "^6.0.0" // 可能安装失败的包
}
}
脚本和命令
scripts
npm 脚本定义。
javascript
{
"scripts": {
"start": "node server.js",
"dev": "nodemon --exec node server.js",
"build": "webpack --mode production",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "eslint src/**/*.js",
"lint:fix": "eslint src/**/*.js --fix",
"type-check": "tsc --noEmit",
"clean": "rimraf dist",
"prepare": "npm run build", // Git hooks 时执行
"pretest": "npm run lint", // test 前执行
"posttest": "npm run coverage", // test 后执行
"prepack": "npm run build", // 打包前执行
"postpack": "npm run clean" // 打包后执行
}
}
发布配置
private
防止意外发布到 npm。
javascript
{
"private": true // 私有包,不会发布到 npm
}
files
指定发布时包含的文件。
javascript
{
"files": [
"dist/", // 包含目录
"lib/**/*.js", // 通配符模式
"README.md", // 具体文件
"LICENSE",
"!**/*.test.js" // 排除文件
]
}
publishConfig
发布配置。
javascript
{
"publishConfig": {
"registry": "https://my-registry.com/", // 自定义 registry
"access": "public", // 公开发布
"tag": "beta" // 发布标签
}
}
项目配置
repository
代码仓库信息。
javascript
{
"repository": {
"type": "git",
"url": "https://github.com/user/project.git",
"directory": "packages/core" // 子目录
},
"repository": "github:user/project", // 简写形式
"repository": "gitlab:user/project"
}
bugs
问题追踪地址。
javascript
{
"bugs": {
"url": "https://github.com/user/project/issues",
"email": "support@example.com"
},
"bugs": "https://github.com/user/project/issues"
}
homepage
项目主页。
javascript
{
"homepage": "https://myproject.com",
"homepage": "https://github.com/user/project#readme"
}
环境和工具配置
engines
指定 Node.js 和 npm 版本要求。
javascript
{
"engines": {
"node": ">=14.0.0", // Node.js 版本
"npm": ">=6.0.0", // npm 版本
"yarn": "^1.22.0" // Yarn 版本
},
"engineStrict": true // 严格检查版本
}
os
指定支持的操作系统。
javascript
{
"os": ["linux", "darwin"], // 支持的系统
"os": ["!win32"] // 排除的系统
}
cpu
指定支持的 CPU 架构。
javascript
{
"cpu": ["x64", "arm64"], // 支持的架构
"cpu": ["!ia32"] // 排除的架构
}
工作空间和 Monorepo
workspaces
工作空间配置 (npm 7+, Yarn)。
javascript
{
"workspaces": [
"packages/*", // 通配符
"apps/web", // 具体路径
"tools/build-utils"
],
"workspaces": {
"packages": ["packages/*"],
"nohoist": ["**/react-native/**"] // Yarn 特定
}
}
其他重要字段
config
配置参数,可通过环境变量访问。
javascript
{
"config": {
"port": 3000, // npm_package_config_port
"api_url": "https://api.example.com"
}
}
funding
资助信息。
javascript
{
"funding": {
"type": "github",
"url": "https://github.com/sponsors/username"
},
"funding": [
"https://opencollective.com/project",
{
"type": "patreon",
"url": "https://patreon.com/project"
}
]
}
browserslist
目标浏览器配置。
javascript
{
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
完整示例
javascript
{
"name": "@myorg/awesome-web-framework",
"version": "2.1.0",
"description": "A modern, type-safe web framework for Node.js",
"keywords": ["web", "framework", "typescript", "api", "server"],
"author": {
"name": "John Doe",
"email": "john@example.com",
"url": "https://johndoe.com"
},
"license": "MIT",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"./middleware": {
"import": "./dist/middleware.mjs",
"require": "./dist/middleware.cjs"
}
},
"bin": {
"awesome-cli": "./bin/cli.js"
},
"files": [
"dist/",
"bin/",
"README.md",
"LICENSE"
],
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src/**/*.ts",
"type-check": "tsc --noEmit",
"prepare": "npm run build",
"prepublishOnly": "npm test && npm run build"
},
"dependencies": {
"express": "^4.18.0",
"cors": "^2.8.5"
},
"devDependencies": {
"@types/express": "^4.17.0",
"@types/node": "^18.0.0",
"typescript": "^4.8.0",
"rollup": "^2.79.0",
"jest": "^28.0.0",
"eslint": "^8.0.0"
},
"peerDependencies": {
"typescript": ">=4.0.0"
},
"engines": {
"node": ">=16.0.0",
"npm": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "https://github.com/myorg/awesome-framework.git"
},
"bugs": "https://github.com/myorg/awesome-framework/issues",
"homepage": "https://awesome-framework.dev",
"funding": "https://github.com/sponsors/myorg"
}