NPM 包开发与优化全面指南

前言

  • Hey, 我是 Immerse
  • 系列文章首发于【Immerse】,更多内容请关注该网站
  • 转载说明:转载请注明原文出处及版权声明!

1. 理解 NPM 包的结构

1.1 package.json 文件:包的核心

package.json文件是 NPM 包的中央配置,定义了包的各个方面,从基本元数据到复杂的发布配置。

{"name": "my-awesome-package","version": "1.0.0","description": "一个令人惊叹的包","main": "./dist/index.js","module": "./dist/index.mjs","types": "./dist/index.d.ts","files": ["dist"],"scripts": {"build": "tsup src/index.ts --format cjs,esm --dts","test": "jest"},"keywords": ["awesome", "package"],"author": "Your Name <you@example.com>","license": "MIT","dependencies": {"lodash": "^4.17.21"},"devDependencies": {"typescript": "^4.5.5","tsup": "^5.11.13","jest": "^27.4.7"}
}

让我们详细解析一些关键字段:

  • nameversion:这两个字段组成了包在 NPM 注册表中的唯一标识符。
  • mainmoduletypes:这些指定了不同模块系统和 TypeScript 支持的入口点。
  • files:这个数组指定了发布包时应该包含哪些文件和目录。
  • scripts:这些是常见任务(如构建和测试)的命令快捷方式。

1.2 理解包的入口点

现代 JavaScript 生态系统支持多种模块格式。您的包应该通过提供多个入口点来适应不同的环境。

  1. main:主要入口点,通常用于 CommonJS (CJS)模块。
  2. module:用于 ECMAScript (ESM)模块的入口点。
  3. browser:用于浏览器环境的入口点。
  4. types:TypeScript 类型声明的入口点。

以下是一个包结构的示例:

my-awesome-package/
├── src/
│   ├── index.ts
│   └── utils.ts
├── dist/
│   ├── index.js        (CJS构建)
│   ├── index.mjs       (ESM构建)
│   ├── index.d.ts      (TypeScript声明)
│   └── browser.js      (浏览器特定构建)
├── package.json
└── tsconfig.json

对应的package.json配置:

{"name": "my-awesome-package","version": "1.0.0","main": "./dist/index.js","module": "./dist/index.mjs","browser": "./dist/browser.js","types": "./dist/index.d.ts","exports": {".": {"require": "./dist/index.js","import": "./dist/index.mjs","types": "./dist/index.d.ts"}}
}

2. 深入理解模块格式

2.1 CommonJS (CJS)

CommonJS 是 Node.js 的传统模块格式。它使用require()进行导入,使用module.exports进行导出。

// mathUtils.js
function add(a, b) {return a + b;
}function subtract(a, b) {return a - b;
}module.exports = {add,subtract,
};// main.js
const mathUtils = require('./mathUtils');
console.log(mathUtils.add(5, 3)); // 输出: 8

2.2 ECMAScript 模块 (ESM)

ESM 是 JavaScript 模块的现代标准,使用importexport语句。

// mathUtils.mjs
export function add(a, b) {return a + b;
}export function subtract(a, b) {return a - b;
}// main.mjs
import { add, subtract } from './mathUtils.mjs';
console.log(add(5, 3)); // 输出: 8

2.3 通用模块定义 (UMD)

UMD 是一种允许模块在多种环境(CommonJS、AMD、全局变量)中工作的模式。

(function (root, factory) {if (typeof define === 'function' && define.amd) {// AMDdefine(['exports'], factory);} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {// CommonJSfactory(exports);} else {// 浏览器全局变量factory((root.mathUtils = {}));}
})(typeof self !== 'undefined' ? self : this, function (exports) {exports.add = function (a, b) {return a + b;};exports.subtract = function (a, b) {return a - b;};
});

3. 高级包优化技术

3.1 Tree Shaking 和副作用

Tree shaking 是现代打包工具用来消除死代码的技术。要使您的包可以进行 tree shaking:

  1. 使用 ES 模块
  2. 避免副作用
  3. package.json中使用"sideEffects"字段
{"name": "my-utils","version": "1.0.0","sideEffects": false
}

如果某些文件确实有副作用:

{"name": "my-utils","version": "1.0.0","sideEffects": ["./src/polyfills.js", "*.css"]
}

3.2 代码分割和动态导入

对于大型包,考虑使用代码分割,允许用户只导入他们需要的部分:

// heavyFunction.js
export function heavyFunction() {// ... 一些计算密集型操作
}// main.js
async function doHeavyWork() {const { heavyFunction } = await import('./heavyFunction.js');heavyFunction();
}

3.3 条件导出

使用条件导出为不同的环境或导入条件提供不同的入口点:

{"name": "my-package","exports": {".": {"import": "./dist/index.mjs","require": "./dist/index.cjs","browser": "./dist/browser.js"},"./utils": {"import": "./dist/utils.mjs","require": "./dist/utils.cjs"}}
}

4. 版本管理和发布

4.1 语义化版本控制 (SemVer)

语义化版本使用三部分版本号:主版本号.次版本号.修订号

  • 主版本号:进行不兼容的 API 更改时
  • 次版本号:以向后兼容的方式添加功能时
  • 修订号:进行向后兼容的 bug 修复时
npm version patch -m "版本更新到 %s - 修复文档中的拼写错误"
npm version minor -m "版本更新到 %s - 添加新的实用函数"
npm version major -m "版本更新到 %s - 更改API结构"

4.2 预发布版本

对于预发布版本,使用带连字符的标签:

  • latest: 最新线上版本
  • alpha: 内部测试版本
  • beta: 公开测试版本
  • rc: 发行候选版本
    • Tips: 可以将这些标识符添加到版本号中,同时也可以添加额外版本:如:1.0.0-alpha.01.0.0-beta.11.0.0-rc.1
npm version prerelease --preid=alpha
# 1.0.0 -> 1.0.1-alpha.0npm version prerelease --preid=beta
# 1.0.1-alpha.0 -> 1.0.1-beta.0npm version prerelease --preid=rc
# 1.0.1-beta.0 -> 1.0.1-rc.0

4.3 使用标签发布

使用标签发布不同版本或预发布版本:

npm publish --tag next
npm publish --tag beta

用户可以安装特定版本:

npm install my-package@next
npm install my-package@beta

5. 持续集成和部署 (CI/CD)

5.1 使用 GitHub Actions 进行自动发布

创建一个.github/workflows/publish.yml文件:

name: 发布包on:release:types: [created]jobs:build:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- uses: actions/setup-node@v2with:node-version: '14'registry-url: 'https://registry.npmjs.org'- run: npm ci- run: npm test- run: npm run build- run: npm publishenv:NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}publish-gpr:needs: buildruns-on: ubuntu-lateststeps:- uses: actions/checkout@v2- uses: actions/setup-node@v2with:node-version: '14'registry-url: 'https://npm.pkg.github.com'- run: npm ci- run: npm publishenv:NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}

这个工作流程将在您创建新版本时自动将您的包发布到 NPM 和 GitHub Packages。

5.2 自动化版本更新

您可以在 CI/CD 管道中自动化版本更新。以下是使用 GitHub Action 的示例:

name: 更新版本on:push:branches:- mainjobs:bump-version:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2with:fetch-depth: 0- uses: actions/setup-node@v2with:node-version: '14'- name: 更新版本run: |git config --local user.email "action@github.com"git config --local user.name "GitHub Action"npm version patch -m "更新版本到 %s [skip ci]"- name: 推送更改uses: ad-m/github-push-action@masterwith:github_token: ${{ secrets.GITHUB_TOKEN }}branch: ${{ github.ref }}

这个动作将在每次向主分支推送更改时自动更新包的修订版本号。

6. 包开发最佳实践

6.1 文档

良好的文档对于包的采用至关重要。考虑使用像 JSDoc 这样的工具进行内联文档:

/*** 将两个数字相加。* @param {number} a - 第一个数字。* @param {number} b - 第二个数字。* @returns {number} a和b的和。*/
function add(a, b) {return a + b;
}

6.2 测试

使用像 Jest 这样的框架实现全面的测试:

// math.js
export function add(a, b) {return a + b;
}// math.test.js
import { add } from './math';test('1 + 2 应该等于 3', () => {expect(add(1, 2)).toBe(3);
});

6.3 代码检查和格式化

使用 ESLint 进行代码检查,使用 Prettier 进行代码格式化。以下是一个示例.eslintrc.js

module.exports = {env: {browser: true,es2021: true,node: true,},extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],parser: '@typescript-eslint/parser',parserOptions: {ecmaVersion: 12,sourceType: 'module',},plugins: ['@typescript-eslint'],rules: {// 在这里添加自定义规则},
};

以及一个.prettierrc文件:

{"singleQuote": true,"trailingComma": "es5","tabWidth": 2,"semi": true,"printWidth": 100
}

info.jpg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/58746.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学Linux的第六天

目录 账户和组管理 工作组管理 创建工作组groupadd 修改工作组groupmod 添加/删除组成员gpasswd 删除工作组groupdel 查看用户登录系统的情况 users查看当前登录系统的用户 last命令 lastlog命令 w命令 显示登录到系统的用户信息 who命令 Linux文件系统权限 文件…

来康生命科技有限公司心率监测解决方案在健身房与康养机构的应用探索

引言 随着科技的日新月异&#xff0c;智能健康服务正逐步成为现代健康管理不可或缺的一环。来康生命科技有限公司&#xff0c;凭借其在智能物联集成交互领域的自主创新能力&#xff0c;推出了一款集蓝牙物联网、蓝牙手环、数据云与管理终端于一体的心率监测解决方案。此方案专…

在 .NET 8 Web API 中实现 Entity Framework 的 Code First 方法

本次介绍分为3篇文章&#xff1a; 1&#xff1a;.Net 8 Web API CRUD 操作.Net 8 Web API CRUD 操作-CSDN博客 2&#xff1a;在 .Net 8 API 中实现 Entity Framework 的 Code First 方法https://blog.csdn.net/hefeng_aspnet/article/details/143229912 3&#xff1a;.NET …

C++类和对象上

1. 类的定义 1.1 类定义格式 • class为定义类的关键字&#xff0c;Stack为类的名字&#xff0c;{}中为类的主体&#xff0c;注意类定义结束时后⾯分号不能省略。类体中内容称为类的成员&#xff1a;类中的变量称为类的属性或成员变量; 类中的函数称为类的⽅法或者成员函数。…

NineData云原生智能数据管理平台新功能发布|2024年10月版

10 月发布内容 本月发布 7 项更新&#xff0c;其中重点发布 2 项、功能优化 3 项、性能优化 1 项、其他发布 1 项。 重点发布​ 数据库 Devops - 数据生成​ NineData 支持在数据库中自动生成符合特定业务场景的随机数据&#xff0c;用于模拟实际生产环境中的数据情况&…

BGP路径属性与路由反射器

前言 IBGP水平分割规则用于防止AS内部产生环路&#xff0c;在很大程度上杜绝了IBGP路由产生环路的可能性&#xff0c;但是同时也带来了新的问题&#xff1a;BGP路由在AS内部只能传递一跳&#xff0c;如果建立IBGP对等体全互联模型又会加重设备的负担。 BGP 路径属性 AS_Path …

医学和生信web APP 平台- Appmatrix

医学&#xff08;和生信&#xff09;web APP 平台- Appmatrix 最近使用shinyproxy将平时所构建的shiny和streamlit医学类应用汇集在一起&#xff0c;实现一站式访问&#xff0c;另外&#xff0c;使用了自己电脑内网穿透&#xff0c;一定程度上缓解了数据分析类APP消耗计算资源…

Rust 力扣 - 1456. 定长子串中元音的最大数目

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们遍历长度为k的窗口&#xff0c;我们只需要记录窗口内的元音字母数量即可&#xff0c;遍历过程中刷新最大数目 题解代码 impl Solution {pub fn max_vowels(s: String, k: i32) -> i32 {let s s.as_byt…

Centos系统新增网卡后获取不到网卡的IP地址解决方法

一、问题描述 当我们给Centos系统添加了新的网卡后,使用查看IP地址命令【ip addr】时,发现新网卡没有获取到对应的IP地址信息,如下图所示: 二、解决方法 有两种解决方法:一种是自动获取IP地址;另外一种是手动配置IP地址; 2.1、自动获取IP地址 #自动获取网卡的IP地址命…

跨境卖家必备!好用的独立站建站工具推荐 !

对于跨境卖家来说&#xff0c;拥有一个独立站是拓展海外市场、提升品牌影响力的重要途径。而选择一款好用的独立站建站工具&#xff0c;则是成功搭建独立站的关键。 一、为什么跨境卖家需要独立站&#xff1f; 在跨境电商领域&#xff0c;独立站具有诸多优势。首先&#xff0c…

Windows Qt 6安装Oracle QOCI SQL Driver插件

本文参考 QOCI for the Oracle Call Interface (OCI)。 Windows 打开 Qt 6.8.0 (MSVC 2022 64-bit)。 Setting up environment for Qt usage… Remember to call vcvarsall.bat to complete environment setup! 执行 "D:\Program Files\Microsoft Visual Studio\2022\E…

书生实战营第四期-第二关python

一、任务1&#xff1a;完成Leetcode 383 1.代码 class Solution:def canConstruct(self,ransomNote: str, magazine: str) -> bool: # 创建一个字典来存储 magazine 中每个字符的出现次数 char_count {} # 遍历 magazine&#xff0c;统计每个字符的出现次数 for char…

计算机考研,选择西安交通大学还是哈工大?

C哥专业提供——计软考研院校选择分析专业课备考指南规划 经过全面分析&#xff0c;2025年考研西安交通大学和哈尔滨工业大学计算机专业的报考难度对比如下&#xff1a; 西安交通大学计算机专业 > 哈尔滨工业大学计算机专业 对于想要报考985高校计算机专业但核心目标是优…

CentOS系统查看CPU、内存、操作系统等信息

Linux系统提供了一系列命令可以用来查看系统硬件信息&#xff0c;如CPU的物理个数、核数、逻辑CPU数量、内存信息和操作系统版本。 查看物理CPU、核数和逻辑CPU 在多核、多线程的系统中&#xff0c;了解物理CPU个数、每个物理CPU的核数和逻辑CPU个数至关重要。超线程技术进一步…

项目升级到.Net8.0 Autofac引发诡异的问题

前两天把项目升级到.Net8.0了&#xff0c;把.Net框架升级了&#xff0c;其他一些第三方库升级了一部分&#xff0c;升级完以后项目跑不起来了&#xff0c;报如下错误&#xff1a; An unhandled exception occurred while processing the request. DependencyResolutionExcepti…

十八、【智能体】数据库:未来科技的大脑

在上一篇中我们讲到了 **变量 ** &#xff0c; 变量 的作用是保存用户个人信息&#xff0c;让 Bot记住用户的特征&#xff0c;使回复更加个性化。 上一篇内容为&#xff1a;https://blog.csdn.net/qq_40585384/article/details/143272599 但变量有一个缺点——存储的信息太单…

web文件包含include

php伪协议 在 PHP 中&#xff0c;伪协议&#xff08;Pseudo Protocols&#xff09; 也被称为 流包装器&#xff0c;这些伪协议以 php:// 开头&#xff0c;后面跟着一些参数&#xff0c;用于指定 要执行的操作 或 需要访问的资源。 伪协议表明这些协议并不是一个 真实的外部协议…

Diving into the STM32 HAL-----USART

如今&#xff0c;电子行业有许多串行通信协议和硬件接口可用。其中之一是通用同步/异步接收器/发射器接口&#xff0c;也简称为 USART。几乎每个微控制器都至少提供一个 UART 外设。几乎所有的 STM32 MCU 都提供至少两个 UART/USART 接口&#xff0c;但根据 MCU 封装支持的 I/O…

功能强大视频编辑软件 Movavi Video Editor Plus 2024 v24.2.0 中文特别版

Movavi Video Editor Plus中文修改版是一款功能强大的视频制作编辑软件&#xff0c;使用能够帮助用户快速从录制的素材中制作成一个精美的电影&#xff0c;支持进行视频剪辑&#xff0c;支持添加背影、音乐和各种音乐&#xff0c;软件使用简单&#xff0c;无需任何的经验和专业…

中国书画、

孙溟㠭浅析“古玺” “古玺”是秦以前印章的通称&#xff0c;远可追溯三代&#xff0c;兴盛于战国。古玺所用的文字是当时六国的篆书&#xff0c;也就是大篆&#xff0c;风格多种多样&#xff0c;变化不一&#xff0c;不容易认识。从文字形式上&#xff0c;可分阴文&#xff08…