1. 创建npm账号
- 官网:www.npmjs.com/
- 创建账号入口:www.npmjs.com/signup
⚠️ 如果之前没有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. 更新包
- npm包修改后,手动把package.json里的version版本号修改了,或者使用以下命令自动更新版本号,再执行npm publish . 命令就可以了。
- 升级补丁版本号(修改bug):npm version patch
- 升级小版本号(新增功能):npm version minor
- 升级大版本号(较大改版):npm version major
- 然后在使用的项目中,重新安装包即可
8. 安装包进行测试
- 为了体验和测试自己上传的包,我们换一个目录
// 在终端输入以下命令,npm-demo-test为测试目录,可以自己任意取名
mkdir npm-demo-test
// 进入测试目录
cd npm-demo-test
- 我们简单写一段js代码,保存到index.js
var demo = require('meta-npm-demo');
var res = demo.sayHi()
console.log(res);
- 执行 npm install npm-demo -D
- 执行 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);