模块化与package.json

模块化与package.json

模块化

CommonJS 模块化规范

CommonJS 是 Node.js 采用的模块化规范,主要用于服务端 JavaScript 开发。它使用同步加载方式,适合服务端环境。

CommonJS 基本用法

CommonJS 使用 require() 函数导入模块,使用 module.exportsexports 导出模块。

导出模块:

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 优化
  • 循环依赖: 可能导致循环依赖问题

模块加载流程:

flowchart TD A[调用require函数] --> B{模块是否在缓存中?} B -->|是| C[返回缓存的模块] B -->|否| D[解析模块路径] D --> E[读取模块文件] E --> F[包装模块代码] F --> G[执行模块代码] G --> H[将模块加入缓存] H --> I[返回module.exports]

ECMAScript Modules (ESM) 规范

ECMAScript Modules (ESM) 是 JavaScript 官方的模块化标准,在现代浏览器和 Node.js 中都得到支持。ESM 使用静态结构,支持异步加载和 Tree Shaking 优化。

ECMAScript Modules 基本用法

ESM 使用 importexport 语句来处理模块的导入和导出。

导出模块:

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 加载流程:

flowchart TD A[解析import语句] --> B[构建模块图] B --> C[检查循环依赖] C --> D[获取模块资源] D --> E[解析模块] E --> F[实例化模块] F --> G[执行模块代码] G --> H[导出绑定] subgraph async ["异步阶段"] D E end subgraph sync ["同步阶段"] F G H end

文件后缀与模块加载规则

在 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 使用以下规则确定模块类型:

模块类型判断流程:

flowchart TD A[读取模块文件] --> B{文件扩展名} B -->|.mjs| C[ESM模块] B -->|.cjs| D[CommonJS模块] B -->|.js| E{查找最近的package.json} E -->|找到| F{检查type字段} E -->|未找到| G[默认CommonJS] F -->|type: module| H[ESM模块] F -->|type: commonjs或未设置| I[CommonJS模块]

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 (明确扩展名)

模块解析优先级:

  1. 文件扩展名 - .mjs.cjs 具有最高优先级
  2. package.json type 字段 - 决定 .js 文件的解析方式
  3. 默认行为 - 未配置时 .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?

选择合适的模块化方案对项目的长期维护和性能至关重要。以下是详细的选择指南和决策流程。

选择决策流程
flowchart TD A[开始新项目] --> B{项目类型} B -->|浏览器端应用| C{是否需要支持老版本浏览器} B -->|Node.js 服务端| D{Node.js版本} B -->|同构应用| E[建议使用ESM + 工具链] C -->|是| F[CommonJS + Babel/Webpack] C -->|否| G[ESM 原生支持] D -->|>= 14.x| H{团队技能} D -->|< 14.x| I[CommonJS] H -->|熟悉现代JS| J[ESM] H -->|传统开发| K[CommonJS] G --> L[现代化方案] J --> L E --> L F --> M[兼容性方案] I --> M K --> M
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:

flowchart LR A[评估项目] --> B[更新依赖] B --> C[修改package.json] C --> D[重构导入导出] D --> E[处理特殊情况] E --> F[测试验证] F --> G[逐步部署] subgraph special ["特殊处理"] E1[__dirname替换] E2[动态require处理] E3[条件加载重构] end E --> E1 E --> E2 E --> E3
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"
}

参考资源

相关推荐
百万蹄蹄向前冲2 小时前
秋天的第一口代码,Trae SOLO开发体验
前端·程序员·trae
努力奋斗12 小时前
VUE-第二季-02
前端·javascript·vue.js
路由侠内网穿透2 小时前
本地部署 SQLite 数据库管理工具 SQLite Browser ( Web ) 并实现外部访问
运维·服务器·开发语言·前端·数据库·sqlite
一只韩非子3 小时前
程序员太难了!Claude 用不了?两招解决!
前端·claude·cursor
JefferyXZF3 小时前
Next.js项目结构解析:理解 App Router 架构(二)
前端·全栈·next.js
Sane3 小时前
react函数组件怎么模拟类组件生命周期?一个 useEffect 搞定
前端·javascript·react.js
gnip3 小时前
可重试接口请求
前端·javascript
烛阴3 小时前
Aspect Ratio -- 宽高比
前端·webgl
若梦plus3 小时前
Node.js中util.promisify原理分析
前端·node.js