如何发布一个自己的npm包?

3,197 阅读5分钟

1. 创建npm账号

⚠️ 如果之前没有npm账号,需要先创建一个npm账号,后面有用到。如果已经有账号,这一步可以忽略。

2. 创建目录

// 在终端输入以下命令,npm-demo为包的目录,可以自己任意取名
mkdir npm-demo
// 进入包目录
cd npm-demo

3. 编写模块

此处我们以一个简单的sayHi为例子,相关代码如下

exports.sayHi = function() {
    return 'Hi, Meta.'
}

将这段代码保存为sayHi.js即可。

4. 初始化包描述文件

在终端执行 npm init 命令,生成package.json文件。如下所示: NPM通过提问似的交互,逐个填入选项,最后生成预览的包描述文件,如果ok,输入yes,此时文件目录下会得到package.json文件。

5. 注册包仓库账号

为了维护包,npm必须要使用仓库账号才允许将包发布到仓库中。

注册账号的命令是 => npm adduser

这也是一个提问似的交互过程,按顺序进行即可。

如果没有进行步骤一创建npm账户,在走npm adduser命令时,该npm账户不存在,或者输入了错误的npm账户信息,会出现如下如下报错信息,所以务必保证输入的用户名&密码&邮箱和步骤一中的npm账号一致:

6. 上传包

上传包的命令是:npm publish 包文件夹名字。

在刚刚创建的package.json文件所在的目录下,执行 => npm publish . 开始上传包

在这个过程中,NPM会将目录打包为一个存档文件,然后上传到官方源仓库中。 此处有个问题需要注意,在生成package.json中,name的名称和项目的名称是保持一致的,如果该npm包名&版本号已经存在,会出现如下报错,此时需要重命名你的package.json中的name。即使你在npm官网没有搜到你预设的包名,它可能也被发布占用过,需要重新考虑。因为我在上传的过程中遇到了这个问题,所以,把package.json中的name改为了meta-npm-demo,包名一定要是唯一的。

7. 更新包

  1. npm包修改后,手动把package.json里的version版本号修改了,或者使用以下命令自动更新版本号,再执行npm publish . 命令就可以了。
  • 升级补丁版本号(修改bug):npm version patch
  • 升级小版本号(新增功能):npm version minor
  • 升级大版本号(较大改版):npm version major
  1. 然后在使用的项目中,重新安装包即可

8. 安装包进行测试

  1. 为了体验和测试自己上传的包,我们换一个目录
// 在终端输入以下命令,npm-demo-test为测试目录,可以自己任意取名
mkdir npm-demo-test
// 进入测试目录
cd npm-demo-test
  1. 我们简单写一段js代码,保存到index.js
var demo = require('meta-npm-demo');
var res = demo.sayHi()
console.log(res);
  1. 执行 npm install npm-demo -D
  2. 执行 node index.js

潜在风险

在企业的内部应用中使用NPM与开源社区中使用有一定的差别。企业的限制在于,一方面需要享受模块开发带来的低耦合和项目组织上的好处,另一方面却要考虑到模块保密性的问题。所以,通过NPM共享和发布存在潜在的风险。

为了同时能够享受到NPM上众多的包,同时对自己的包进行保密和限制,现有的解决方案就是企业搭建自己的NPM仓库。局域NPM仓库的搭建方法与搭建镜像站的方式几乎一样,与镜像仓库不同的地方在于,企业局域NPM可以选择不同步官方源仓库中的包,即企业可混合使用官方仓库和局域仓库。

对于企业内部而言,私有的可重用模块可以打包到局域NPM仓库中,这样可以保持更新的中心化,不至于让各个小项目各自维护相同功能的模块,杜绝通过复制粘贴实现代码共享的行为。

搭建局域NPM仓库,有时间会另写一篇。

模块的侧重点

浏览器端的JavaScript,需要通过网络加载代码,而服务器端的JavaScript从磁盘中加载。Node的模块引入过程几乎都是同步的,但是如果前端模块也采用同步的方式来引入,那UI在初始化过程中需要花费很多时间来等待脚本加载完成。

鉴于网络的原因,CommonJs为后端JavaScript制定的规范并不完全适合前端的应用场景。最终出现了AMD规范和CMD规范。

所以为了让同一个模块可以运行在前后端,在写的过程中需要考虑兼容前端模块规范。为了保持前后端的一致性,累哭开发者需要将类库代码包装在一个闭包内。

所以我们可以用以下写法兼容Node、AMD、CMD以及常见的浏览器环境中:

;(function(global) {
	"use strict";
    var definition = function(opts) {
        console.log(opts);
    };
	// 检测上下文环境是否为AMD或CMD
    var hasDefine = typeof define === 'function';
    // 检查上下文环境是否为Node
    var hasExports = typeof module !== 'undefined' && module.exports;
    if (hasDefine) {
    	// AMD环境或者CMD环境
        define(function() {
        	return definition;
        });
    } else if (hasExports) {
    	// 定义为普通node模块
        module.exports = definition();
    } else {
    	// 将模块的执行结果挂在window变量中,在浏览器中this指向window对象
        global.definition = definition();
    }
})(this);