Node.js 系列 -- npm 包管理器

1,123 阅读5分钟

前言

我们在开发前端项目时通常会使用到别人封装好的代码,比如 demo / 函数 / 组件库 / 框架 / 插件 等,我们通常会选择:npm 方式来安装依赖。那 npm 到底是森么

npm 简介

npm 是 Node.js 标准的软件包管理器。

在 2017 年 1 月时,npm 仓库中就已有超过 350000 个软件包,这使其成为世界上最大的单一语言代码仓库,并且可以确定几乎有可用于一切的软件包。

它起初是作为下载和管理 Node.js 包依赖的方式,但其现在也已成为前端 JavaScript 中使用的工具。

NPM 能很好地和诸如 webpackBrowserify 模块打包器配合使用

在用 Vue 构建大型应用时推荐使用 NPM 安装。同时 Vue 也提供配套工具来开发单文件组件

npm 常见的 使用场景 有以下几种:

  • 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
  • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
  • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

npm 功能

1. 安装软件包(install)

npm 可以管理项目依赖的下载

a. 安装所有依赖

如果项目具有 package.json 文件,则通过运行:

npm install

它会在 node_modules 文件夹(如果尚不存在则会创建)中安装项目所需的所有东西

b. 安装单个软件包

运行以下命令安装特定的软件包:

npm install <package-name> --save
npm install <package-name> --save-dev
  • --save 安装并添加条目到 package.json 文件的 dependencies【与生产环境中的应用程序相关】
  • --save-dev 安装并添加条目到 package.json 文件的 devDependencies【开发的工具(例如测试的库)】

2. 更新软件包(update)

通过运行以下命令,更新也变得很容易:

npm update

npm 会检查所有软件包是否有满足版本限制的更新版本。

也可以指定单个软件包进行更新:

npm update <package-name>

3. 运行任务(run)

package.json 文件支持一种用于指定命令行任务(可通过使用以下方式运行)的格式:

npm run <task-name>

例如:一个 vue 项目的 package.json 文件中:

{
    "scripts": {
        "serve": "vue-cli-service serve",
        "build": "vue-cli-service build",
         ...
    }
}

我们通过在终端运行

npm run serve

可以使得 serve 任务,使得我们的本地 vue-cli 项目可以通过 http://localhost:8080/#/ 这个 URL 在浏览器中运行,这常用于我们对前端项目进行调试

如果我们想让我们的前端项目在运行调试前对软件包进行更新,我们可以在 package.json 文件中 scripts 对象的 serve 字段进行修改:

{
    "scripts": {
        "serve": "node node_modules/<package-name>/update && vue-cli-service serve",
        "build": "vue-cli-service build",
         ...
    }
}

如何使用或执行 npm 安装的软件包

假设使用以下命令安装了流行的 JavaScript 实用工具库 lodash:

npm install lodash

这会把软件包安装到本地的 node_modules 文件夹中。

若要在代码中使用它,则只需使用 require / import 将其导入到程序中:

const _ = require('lodash')
或
import _ from 'lodash'

另外,比如使用 vue + webpack 套餐的话,要全局引入组件库,则需要在main.js中写入:

import Vue from 'vue';
import router from './router';
import store from './store';

// 引入组件库
import PackageName from '<package-name>';
import '<package-name>/lib/index.css';

Vue.use(PackageName);

const app = new Vue({
  store,
  router,
  render: (h) => h(App),
}).$mount('#app');

window.app = app;

npm 版本问题

版本控制

除了简单的下载外,npm 还可以管理版本控制,因此可以指定安装软件包的任何特定版本,或者要求版本高于或低于所需版本。

npm install <package-name> <version>

包版本可以有三种写法:

  • "packageName": "15.2.1" 只匹配一个版本,表示:只下载15.2.1 的版本
  • "packageName": "~15.2.1" 匹配最近的小版本依赖包,表示下载>= 15.2.1 && < 15.3.0的版本
  • "packageName": "^15.2.1" 匹配最新的大版本依赖包,表示下载>= 15.2.1 && < 16.0.0的版本

很多时候,一个库仅与另一个库的主版本兼容。或者,一个库的最新版本中有一个缺陷(仍未修复)引起了问题。

指定库的显式版本还有助于使每个人都使用相同的软件包版本,以便 整个团队运行相同的版本,直至 package.json 文件被更新。

如何查看 npm 包安装的版本

若要查看所有已安装的 npm 软件包(包括它们的依赖包)的最新版本,则:

npm list

例如:

❯ npm list
/Users/joe/dev/node/cowsay
└─┬ cowsay@1.3.1
  ├── get-stdin@5.0.1
  ├─┬ optimist@0.6.1
  │ ├── minimist@0.0.10
  │ └── wordwrap@0.0.3
  ├─┬ string-width@2.1.1
  │ ├── is-fullwidth-code-point@2.0.0
  │ └─┬ strip-ansi@4.0.0
  │   └── ansi-regex@3.0.0
  └── strip-eof@1.0.0

安装历史版本

可以使用 @ 语法来安装 npm 软件包的旧版本:

npm install <package>@<version>

可能还有需要列出软件包所有的以前的版本。 可以使用 npm view <package> versions

❯ npm view cowsay versions

[ '1.0.0',
  '1.0.1',
  '1.0.2',
  '1.0.3',
  '1.1.0',
  '1.1.1',
  '1.1.2',
  '1.1.3',
  '1.1.4',
  '1.1.5',
  '1.1.6',
  '1.1.7',
  '1.1.8',
  '1.1.9',
  '1.2.0',
  '1.2.1',
  '1.3.0',
  '1.3.1' ]

卸载 npm 软件包

若要卸载之前在本地安装(在 node_modules 文件夹使用 npm install <package-name>)的软件包,则从项目的根文件夹(包含 node_modules 文件夹的文件夹)中运行:

npm uninstall <package-name>

如果使用 -S 或 --save 标志,则此操作还会移除 package.json 文件中的引用。

如果程序包是开发依赖项(列出在 package.json 文件的 devDependencies 中),则必须使用 -D 或 --save-dev 标志从文件中移除:

npm uninstall -S <package-name>
npm uninstall -D <package-name>

如果该软件包是全局安装的,则需要添加 -g 或 --global 标志:

npm uninstall -g <package-name>

package.json 指南

package.json 位于模块的目录下,用于 定义包的属性

package.json 属性说明

  • name - 包名。
  • version - 包的版本号。
  • description - 包的描述。
  • homepage - 包的官网 url 。
  • author - 包的作者姓名。
  • scripts - 执行脚本。
  • contributors - 包的其他贡献者姓名。
  • dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。
  • repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。
  • main - main 字段指定了程序的主入口文件,require('moduleName') 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。
  • keywords - 关键字

看例子

{
  "name": "express",
  "description": "Fast, unopinionated, minimalist web framework",
  "version": "4.13.3",
  "author": {
    "name": "TJ Holowaychuk",
    "email": "tj@vision-media.ca"
  },
  "contributors": [
    {
      "name": "Aaron Heckmann",
      "email": "aaron.heckmann+github@gmail.com"
    },
    {
      "name": "Ciaran Jessup",
      "email": "ciaranj@gmail.com"
    }
  ],
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/strongloop/express.git"
  },
  "homepage": "http://expressjs.com/",
  "keywords": [
    "express",
    "framework",
    "sinatra",
    "web",
    "rest",
    "restful",
    "router",
    "app",
    "api"
  ],
  "dependencies": {
    "accepts": "~1.2.12",
    "array-flatten": "1.1.1",
    "content-disposition": "0.5.0",
    "content-type": "~1.0.1",
    "cookie": "0.1.3",
    "cookie-signature": "1.0.6",
    "debug": "~2.2.0",
    "depd": "~1.0.1",
    "escape-html": "1.0.2",
    "etag": "~1.7.0",
    "finalhandler": "0.4.0",
    "fresh": "0.3.0",
    "merge-descriptors": "1.0.0",
    "methods": "~1.1.1",
    "on-finished": "~2.3.0",
    "parseurl": "~1.3.0",
    "path-to-regexp": "0.1.7",
    "proxy-addr": "~1.0.8",
    "qs": "4.0.0",
    "range-parser": "~1.0.2",
    "send": "0.13.0",
    "serve-static": "~1.10.0",
    "type-is": "~1.6.6",
    "utils-merge": "1.0.0",
    "vary": "~1.0.1"
  },
  "devDependencies": {
    "after": "0.8.1",
    "ejs": "2.3.3",
    "istanbul": "0.3.17",
    "marked": "0.3.5",
    "mocha": "2.2.5",
    "should": "7.0.2",
    "supertest": "1.0.1",
    "body-parser": "~1.13.3",
    "connect-redis": "~2.4.1",
    "cookie-parser": "~1.3.5",
    "cookie-session": "~1.2.0",
    "express-session": "~1.11.3",
    "jade": "~1.11.0",
    "method-override": "~2.3.5",
    "morgan": "~1.6.1",
    "multiparty": "~4.1.2",
    "vhost": "~3.0.1"
  },
  "engines": {
    "node": ">= 0.10.0"
  },
  "files": [
    "LICENSE",
    "History.md",
    "Readme.md",
    "index.js",
    "lib/"
  ],
  "scripts": {
    "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/"
  },
  "gitHead": "ef7ad681b245fba023843ce94f6bcb8e275bbb8e",
  "bugs": {
    "url": "https://github.com/strongloop/express/issues"
  },
  "_id": "express@4.13.3",
  "_shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3",
  "_from": "express@*",
  "_npmVersion": "1.4.28",
  "_npmUser": {
    "name": "dougwilson",
    "email": "doug@somethingdoug.com"
  },
  "maintainers": [
    {
      "name": "tjholowaychuk",
      "email": "tj@vision-media.ca"
    },
    {
      "name": "jongleberry",
      "email": "jonathanrichardong@gmail.com"
    }
  ],
  "dist": {
    "shasum": "ddb2f1fb4502bf33598d2b032b037960ca6c80a3",
    "tarball": "http://registry.npmjs.org/express/-/express-4.13.3.tgz"
  },
  "directories": {},
  "_resolved": "https://registry.npmjs.org/express/-/express-4.13.3.tgz",
  "readme": "ERROR: No README data found!"
}

参考文章