Node 的生态NPM

  • npm i 【npm install】

安装

  • npm install npm -g

npm的三个部分

  • npm 官网 www.npmjs.com 网站直接查询一个模块的相关信息
  • npm registry https://registry.npmjs.org/ 模块查询下载的服务
  • npm cli 命令行下载工具

npm registry 【注册表】

npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install [email protected]

团队搭建私有 registry

  • cnpmjs.org
  • 阿里云的 regsitry Aliyun Registry

初始化

  • npm init
  • npm init --yes

安装

  • npm install

运行时依赖、本地开发依赖

  • --save、--save-dev 安装到 dependencies【运行时依赖的模块】 和 devDependencies【本地开发时依赖的模块】
  • 简写 npm i xx -Snpm i xx -D

npm help install

~ npm help install
# 项目中已有 package.json,可以直接 npm intall 安装所有依赖项
npm install (with no args, in package dir)
# scope 通常用于管理私有模块,以 @ 开头,没有 @ 则反之
# npm install some-pkg
# npm install @scott/some-pkg
npm install [<@scope>/]
# 可以安装特定 tag 的模块,默认是 latest,如:
# npm install lodash@latest
npm install [<@scope>/]@
# npm install [email protected]
# npm install @scott/[email protected]
npm install [<@scope>/]@
# 安装一个某个范围内的版本
# npm install lodash@">=2.0.0 <3.0.0"
# npm install @scott/som-pkg@">=2.0.0 <3.0.0"
npm install [<@scope>/]@
# npm install git+ssh://[email protected]:tj/commander.js.git
npm install :/
# 以 git 仓库地址来安装
# npm install https://github.com/petkaantonov/bluebird.git
npm install 
# 安装本地的 tar 包
# npm install /Users/black/Downloads/request-2.88.1.tar.gz
npm install 
# 以 tar 包地址来安装
# npm install https://github.com/caolan/async/tarball/v2.3.0
# npm install https://github.com/koajs/koa/archive/2.5.3.tar.gz
npm install 
# 从本地文件夹安装
# npm install ../scott/some-module
npm install 
# 卸载也很简单
npm uninstall some-pkg -S
# 或者简写,加上 -S 是把卸载也同步到 package.json 中
npm un some-pkg -S

npm semver version

  • package.json
"dependencies": {
  "bluebird": "^3.5.2",
  "lodash": "^4.17.11"
}
  • node_modules
npm tree -L 2
.
├── node_modules
│   ├── bluebird
│   └── lodash
├── package-lock.json
└── package.json

版本管理

  • Semantic Versioning 2.0.0

  • semver.org
    版本号比如 v4.5.1,v 是 version 的缩写,4.5.1 被 . 分开成三端,这三端分别是:major minor patch,也就是 主版本号.次版本号.修订号

  • major: breaking changes (做了不兼容的 API 修改)

  • minor: feature add(向下兼容的功能性新增)

  • patch: bug fix, docs(向下兼容的问题修正)

node_modules

  • 目录是递归安装的,它是按照依赖关系进行文件夹的嵌套
~ tree -L 4
.
├── connect-mongo
│   ├── node_modules
│   │   └── mongodb
│   │       ├── node_modules
├── mongoose
│   ├── node_modules
│   │   ├── mongodb
│   │   │   └── node_modules
│   │   └── sliced
├── async
├── grunt
│   ├── node_modules
│   │   ├── async
│   │   └── which
└── underscore

npm3 时代里面策略改成了平铺结构

➜  node_modules tree -L 1
.
├── ajv
├── asn1
├── assert-plus
├── asynckit
├── aws-sign2
├── aws4
├── bcrypt-pbkdf
├── bluebird
├── caseless
├── co
├── combined-stream
├── delayed-stream
├── fast-deep-equal
├── fast-json-stable-stringify
├── forever-agent
├── form-data
├── uuid
└── ...省略剩下 20 个

npm shrinkwrap 锁包

  • package-lock文件 【npm5以后的新特性】
  • 在一个 package.json 里的 dependencies 里面,包的依赖版本可以这样写:
"lodash": "~3.9.0",
"lodash": "^3.9.0",
"lodash": ">3.9.0",
"lodash": ">=1.0.0-rc.2",
"lodash": "*"
// ... 更多写法不再列举
  • ~ 意思是,选择一个最近的小版本依赖包,比如 ~3.9.0 可以匹配到所有的 3.9.x 版本,但是不会匹配到 3.10.0
  • ^ 则是匹配最新的大版本,比如 ^3.9.0 可以匹配到所有的 3.x.x,但是不会匹配到 4.0.0
  • npm init --yes && npm i lodash async -S

npm scripts

  • 在 package.json 里的 scripts 里配置的各种任务,都可以这样直接调用:
npm start
npm run dev
npm run egg:prod
"scripts": {
  "build": "npm run build:prod",
  "clean:dist": "rimraf ./dist",
  "build:prod": "cross-env NODE_ENV=production webpack"
}

# 如下命令行均可执行
➜  npm run clean:dist
➜  npm run build:prod
➜  npm run build
"scripts": {
  // 通过 && 分隔,如果 clean:dist 任务失败,则不会执行后面的构建任务
  "build:task1": "npm run clean:dist && npm run build:prod"
  // 通过 ; 分隔,无论 clean:dist 是否成功,运行后都继续执行后面的构建任务
  "build:task2": "npm run clean:dist;npm run build:prod"
  // 通过 || 分隔,只有当 clean:dist 失败,才会继续执行后面的构建任务
  "build:task3": "npm run clean:dist;npm run build:prod"
  "clean:dist": "rimraf ./dist",
  "build:prod": "cross-env NODE_ENV=production webpack",
  // 对一个命令传配置参数,可以通过 -- --prod
  // 比如 npm run compile:prod 相当于执行 node ./r.js --prod
  "compile:prod": "npm run compile -- --prod",
  "compile": "node ./r.js",
}

npx

  • npx 是 npm 自带的非常酷炫的功能,直接执行依赖包里的二进制文件
# 先安装一个 cowsay
➜  npm install cowsay -D
# 包里的二进制文件会被放到 node_modules/.bin 目录下

➜  ll node_modules/.bin/
total 0
lrwxr-xr-x 1 16:34 cowsay -> ../cowsay/cli.js
lrwxr-xr-x 1 16:34 cowthink -> ../cowsay/cli.js

# 直接通过 npx 来调用 cowsay 里的二进制文件
➜  npx cowthink Node 好玩么
 _____________
( Node 好玩么 )
 -------------
        o   ^__^
         o  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

➜  npx cowsay 爽爆了
 _____________
< Node 爽爆了 >
 -------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

实现一个 Node LTS 查看工具

项目初始化

➜  cd ltsn
# 通过 touch 新建一个 markdown 的文件,用来描述包功能
➜  touch README.md
# 通过 touch 新建一个 git 忽略文件
➜  touch .gitignore

.gitignore

.DS_Store
npm-debug.log
node_modules
yarn-error.log
.vscode
.eslintrc.json

npm init 生成 package.json

  • npm init:
➜  ltsn npm init
Press ^C at any time to quit.
# 回车确认或者输入另外一个名字作为包名
package name: (ltsn)
# 版本就从 1.0.0 开始
version: (1.0.0)
# 简单的描述
description: CommandLine Tool for Node LTS
# 包的入口文件地址,通过 index.js 暴露内部函数
entry point: (index.js) index.js
# 测试脚本,可以先留空,大家根据实际情况取舍
test command:
# 包的 github 仓库地址
git repository: [email protected]:4liang/ltsn.git
# 一些功能关键词描述
keywords: Node LTS
# 作者自己
author: 4liang
# 开源的协议,默认是 ISC,我个人喜欢 MIT
license: (ISC) MIT
# 检查信息无误,输入 yes 回车即可
About to write to /Users/4liang/juejin/ltsn/package.json:
{
  "name": "ltsn",
  "version": "1.0.0",
  "description": "CommandLine Tool for Node LTS",
  "main": "lib/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "[email protected]:4liang/ltsn.git"
  },
  "keywords": [
    "Node", "LTS"
  ],
  "author": "4liang",
  "license": "MIT",
  "bugs": { "url": "https://github.com/4liang/ltsn/issues" },
  "homepage": "https://github.com/4liang/ltsn#readme"
}

Is this OK? (yes) yes

# 输入 ls 查看当前包内文件
➜  ls
package.json README.md

脚手架

npm i yo generator-nm -g

目录 /lib

exports.query = require('./lib/query')
exports.update = require('./lib/update')

/lib/update.js 可以用来放数据源的获取和更新,而 /lib/query.js 里面可以放对数据的二次加工格式化之类,首先是 /lib/update.js 获取 Node LTS 数据:

const axios = require('axios')
const color = require('cli-color')
const terminalLink = require('terminal-link')
const compareVersions = require('compare-versions')

module.exports = async (v) => {
  // 拿到所有的 Node 版本
  const { data } = await axios
    .get('https://nodejs.org/dist/index.json')
  
  // 把目标版本的 LTS 都挑选出来
  return data.filter(node => {
    const cp = v
      ? (compareVersions(node.version, 'v' + v + '.0.0') >= 0)
      : true
    return node.lts && cp
  }).map(it => {
    // 踢出去 file 这个字段,其他的全部返回
    const { files, ...rest } = it
    return { ...rest }
  })
}

然后是 /lib/query.js:

const Table = require('cli-table')

function query(dists) {
  const keys = Object.keys(dists[0])
  // 建立表头
  const table = new Table({
    head: keys
  })
  
  // 拼接出表格的每一行
  return dists
    .reduce((res, item) => {
      table.push(
        Object.values(item)
      )
      return res
    }, table)
    .toString()
}

module.exports = query

最后,再增加一个 bin 文件夹,在它里面增加一个 ltsn 脚本文件,在里面写入:

#!/usr/bin/env node

const pkg = require('../package')
// 从顶层 index.js 里面拿到 lib 下面模块暴露的方法
const query = require('..').query
const update = require('..').update

// 输出结果到命令行窗口
function printResult(v) {
  update(v).then(dists => {
    const results = query(dists, v)
    console.log(results)
    process.exit()
  })
}

function printVersion() {
  console.log('ltsn ' + pkg.version)
  process.exit()
}

// 一些命令的帮助提示
function printHelp(code) {
  const lines = [
    '',
    '  Usage:',
    '    ltsn [8]',
    '',
    '  Options:',
    '    -v, --version             print the version of vc',
    '    -h, --help                display this message',
    '',
    '  Examples:',
    '    $ ltsn 8',
    ''
  ]

  console.log(lines.join('\n'))
  process.exit(code || 0)
}

// 包的入口函数,里面对参数做剪裁处理,拿到入参并给予
// 不同入参的处理逻辑
function main(argv) {
  if (!argv) {
    printHelp(1)
  }

  const getArg = function() {
    let args = argv.shift()

    args = args.split('=')
    if (args.length > 1) {
      argv.unshift(args.slice(1).join('='))
    }
    return args[0]
  }

  let arg

  while (argv.length) {
    arg = getArg()
    switch(arg) {
      case '-v':
      case '-V':
      case '--version':
        printVersion()

        break
      case '-h':
      case '-H':
      case '--help':
        printHelp()

        break
      default:
        printResult(arg)

        break
    }
  }
}

// 启动程序就开始执行主函数
main(process.argv.slice(2))

module.exports = main

#!/usr/bin/env node 加上 #! 这里是定义当前脚本的执行环境是用 Node 执行,安装包以后我们希望它可以像一个二进制一样来执行,那么可以到 package.json 来配置下执行路径,在 package.json 里面增加一个配置属性:

"bin": {
  "ltsn": "bin/ltsn"
},

然后对于用到的模块,我们在包目录下,执行:

npm i axios cli-color cli-table compare-versions -S

这样安装后,package-lock.json 也自动创建了, 整个的目录结果如下:

~ tree -L 2
.
├── README.md
├── bin
│   └── ltsn
├── index.js
├── lib
│   ├── query.js
│   └── update.js
├── node_modules
├── package-lock.json
└── package.json

再把 README.md 文档内容完善一下,我们的代码就准备好了。

npm install 本地包进行测试

等到代码写完,就可以本地测试了,本地测试最简单的办法,就是通过 npm link 安装下:

~ npm link
npm WARN [email protected] No repository field.

audited 145 packages in 2.103s
found 0 vulnerabilities

/Users/4liang/.nvm/versions/node/v10.11.0/bin/ltsn -> /Users/4liang/.nvm/versions/node/v10.11.0/lib/node_modules/ltsn/bin/ltsn
/Users/4liang/.nvm/versions/node/v10.11.0/lib/node_modules/ltsn -> /Users/4liang/juejin/ltsn

然后边调试代码边测试,测试完毕后,可以直接在本地指定目录来全局安装,首先卸载掉之前可能测试安装过的全局包:

npm uninstall ltsn -g

然后可以在命令行窗口用绝对路径,或者直接进入到包目录下,执行全局安装动作:

npm i ./ -g
# npm i /Users/4liang/juejin/ltsn -g
image.png

npm publish 发布包

  • 到 npmjs.com 注册好一个账号且邮箱验证完

  • npm login

  • npm publish

npm install 线上包进行验证

~ npm uninstall ltsn -g
removed 1 package in 0.262s

~ npm i ltsn -g
/Users/black/.nvm/versions/node/v10.13.0/bin/ltsn -> /Users/black/.nvm/versions/node/v10.13.0/lib/node_modules/ltsn/bin/ltsn
+ [email protected]
added 28 packages from 17 contributors in 5.33s

你可能感兴趣的:(Node 的生态NPM)