如前文所述,Node.js 使用了模块化管理的方式,使得引入其他 JS 包、框架、小工具等等都非常的容易和舒服;同样 NPM 的出现,使得我们不需要四处去寻找对应 JS 包、框架、小工具的官方网站进行下载,只要知道名字,我们直接通过一行或多行命令就可以搜索到并直接下载安装,整个过程的体验非常好;说到这里,我真的也很喜欢 Python 的 PIP,哈哈。
当然,除了 Node.js 自带的 NPM 包管理器,还有一些比较常用的第三方包管理器,比如 yarn(来自 Facebook)或者 bower 等等,当然其他的包管理器就不如这三个这么知名并且使用人数多了。
今天主要集中在 NPM 上,其实为了保持使用体验的一致性,包管理器的使用方法基本都是一致的,它们的区别主要在一些优化方面,比如安装速度、模块管理的便利性等等,所以每个开发人员都有自己偏爱的包管理器——我一般都是使用 NPM 的;当然也因为 SPFx (SharePoint Framework)的安装指南,用过 yarn,这个其实用起来也很容易。
絮叨了半天,接下来是正文。
1 简介
NPM 是随同 Node.js
- 允许用户从 NPM 服务器下载别人编写的第三方包到本地使用。
- 允许用户从 NPM 服务器下载并安装别人编写的命令行程序到本地使用。
- 允许用户将自己编写的包或命令行程序上传到 NPM 服务器供别人使用。
由于 Node.js 已经集成了 NPM,所以之前 NPM 也一并安装好了。同样可以通过输入 "npm -v"
如果已经安装了旧版本的 NPM,可以很容易得通过 npm i npm -g 或者 npm install npm -g 命令来升级,这里 npm i 是 npm install
如果是 Linux 系统,则需要使用以下命令:
sudo npm install npm -g
# 或者
sudo npm i npm -g
2 使用 npm 命令安装模块
NPM 安装 Node.js
npm install <Module Name>
以下实例,我们使用 npm 命令安装常用的 Node.js web 框架模块 express:
npm install express
安装好之后,express 包就放在了工程目录下的 node_modules 目录中,因此在代码中只需要通过 require('express')
const express = require('express');
3 全局安装与本地安装
NPM 的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有 -g
npm install express # 本地安装
npm install express -g # 全局安装
如果出现以下错误:
npm err! Error: connect ECONNREFUSED 127.0.0.1:8087
解决办法为:
npm config set proxy null
3.1 本地安装
- 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules
- 可以通过 require()
3.2 全局安装
- 将安装包放在 /usr/local 下或者你 node 的安装目录。
- 可以直接在命令行里使用。
如果你希望具备两者功能,则需要在两个地方安装它或使用 npm link。
接下来我们使用全局方式安装 express:
npm install express -g
安装过程输出如下内容:
和以前不同的是,现在的显示内容更加简单,仅仅会告知是否成功,如果有错误则显示错误信息(红色)或者警告信息(黄色)。
3.3 查看安装信息
我们可以使用以下命令来查看所有全局安装的模块:
$ npm list -g
当然如果装的模块非常多,那就需要一定时间进行查找和响应,比如我这里:
并且如果有模块需要更新,或者其他错误,还会显示出报错信息:
如果要查看某个模块的版本号,可以使用命令 npm list <module name>,如下:
4 使用 package.json
package.json
接下来让我们来看下 express 包的 package.json 文件,位于 node_modules/express/package.json
{
"_from": "express",
"_id": "express@4.17.1",
"_inBundle": false,
"_integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"_location": "/express",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "express",
"name": "express",
"escapedName": "express",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#DEV:/",
"#USER"
],
"_resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"_shasum": "4491fc38605cf51f8629d39c2b5d026f98a4c134",
"_spec": "express",
"_where": "C:Usersmichadocumentsgitnode",
"author": {
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca"
},
"bugs": {
"url": "https://github.com/expressjs/express/issues"
},
"bundleDependencies": false,
"contributors": [
{
"name": "Aaron Heckmann",
"email": "aaron.heckmann+github@gmail.com"
},
{
"name": "Ciaran Jessup",
"email": "ciaranj@gmail.com"
},
{
"name": "Douglas Christopher Wilson",
"email": "doug@somethingdoug.com"
},
{
"name": "Guillermo Rauch",
"email": "rauchg@gmail.com"
},
{
"name": "Jonathan Ong",
"email": "me@jongleberry.com"
},
{
"name": "Roman Shtylman",
"email": "shtylman+expressjs@gmail.com"
},
{
"name": "Young Jae Sim",
"email": "hanul@hanul.me"
}
],
"dependencies": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"deprecated": false,
"description": "Fast, unopinionated, minimalist web framework",
"devDependencies": {
"after": "0.8.2",
"connect-redis": "3.4.1",
"cookie-parser": "~1.4.4",
"cookie-session": "1.3.3",
"ejs": "2.6.1",
"eslint": "2.13.1",
"express-session": "1.16.1",
"hbs": "4.0.4",
"istanbul": "0.4.5",
"marked": "0.6.2",
"method-override": "3.0.0",
"mocha": "5.2.0",
"morgan": "1.9.1",
"multiparty": "4.2.1",
"pbkdf2-password": "1.2.1",
"should": "13.2.3",
"supertest": "3.3.0",
"vhost": "~3.0.2"
},
"engines": {
"node": ">= 0.10.0"
},
"files": [
"LICENSE",
"History.md",
"Readme.md",
"index.js",
"lib/"
],
"homepage": "http://expressjs.com/",
"keywords": [
"express",
"framework",
"sinatra",
"web",
"rest",
"restful",
"router",
"app",
"api"
],
"license": "MIT",
"name": "express",
"repository": {
"type": "git",
"url": "git+https://github.com/expressjs/express.git"
},
"scripts": {
"lint": "eslint .",
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
},
"version": "4.17.1"
}
Package.json 属性说明
- name:包名
- version:包的版本号
- description:包的描述
- homepage:包的官网 url
- author:包的作者姓名
- contributors:包的其他贡献者姓名
- dependencies:依赖包列表;如果依赖包没有安装,NPM 会自动将依赖包安装在 node_module
- repository:包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上
- main:main 字段指定了程序的主入口文件,require('moduleName') 就会加载这个文件;这个字段的默认值是模块根目录下面的 index.js
- keywords
5 卸载模块
我们可以使用以下命令来卸载 Node.js
npm uninstall express
卸载后,可以到 /node_modules/
npm ls
6 更新模块
我们可以使用以下命令更新模块:
$ npm update express
7 搜索模块
使用以下命令来搜索模块:
npm search express
8 创建模块
创建模块,package.json 文件是必不可少的,我们可以使用 npm init 命令生成 package.json
以上的信息,需要根据我们自己的情况输入,在最后回车(默认输入 yes) 后会生成 package.json
接下来我们可以使用以下命令在 npm 资源库中注册用户(使用邮箱注册):
npm adduser
Username: michaeljeancn
Password:
Email: (this IS public) michaeljeancn@outlook.com
接下来我们就用以下命令来发布模块:
npm publish
如果以上的步骤都操作正确,我们就可以跟其他模块一样使用 npm 来安装自己发布的模块了。
9 版本号
使用 NPM 下载和发布代码时都会接触到版本号,NPM 使用语义版本号来管理代码,这里简单介绍一下。
语义版本号分为 X.Y.Z
- 如果只是修复 bug,需要更新 Z 位。
- 如果是新增了功能,但是向下兼容,需要更新 Y 位。
- 如果有大变动,向下不兼容,需要更新 X 位。
版本号有了这个保证后,在申明第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号,例如 "argv": "0.0.x" 表示依赖于 0.0.x 系列的最新版 argv。
NPM 支持的所有版本号范围指定方式可以查看官方文档。
10 NPM 常用命令
除了本章节介绍的部分外,NPM 还提供了很多功能,package.json
除了可以在 npmjs.org/doc/ 查看官方文档外,这里再介绍一些 NPM 常用命令:
- NPM 提供了很多命令,例如 install 和 publish,使用 npm help
- 使用 npm help <command> 可查看某条命令的详细帮助,例如 npm help install
- 在 package.json 所在目录下使用 npm install . -g 可先在本地安装当前命令行程序,可用于发布前的本地测试
- 使用 npm update <package> 可以把当前目录下 node_modules
- 使用 npm update <package> -g
- 使用 npm cache clear 可以清空 NPM 本地缓存,用于对付使用相同版本号发布新版本代码的人
- 使用 npm unpublish <package>@<version>
11 使用淘宝 NPM 镜像
大家都知道国内直接使用 npm 的官方镜像是非常慢的,在不涉及到“墙”的情况下,我们可以考虑使用淘宝 NPM 镜像。
淘宝 NPM 镜像是一个完整 http://npmjs.org 镜像,可以用此代替官方版本(只读),同步频率目前为 10分钟
我个人比较喜欢使用淘宝定制的 cnpm(gzip 压缩支持)命令行工具代替默认的 npm:
npm install -g cnpm --registry=https://registry.npm.taobao.org
这样就可以使用 cnpm 命令来安装模块了:
cnpm install [name]