npm node package manager
nodejs的包管理工具

什么是 npm?
就是别人写的 nodejs模块,可以直接拿来使用

npm文档 https://www.npmjs.cn/getting-started/creating-node-modules

image.png

  1. npm, Node Package Manager
  2. npm包是从哪里下载的?,就是 registry
    1. npm.com 为企业提供私有的 npm registry 服务和团队合作的 Saas 服务
    2. npm包就是:一个特定功能的文件夹;包是模块的集合
  1. npm i lodash --registry=https://registry.npm.taobao.org
  2. which node
  3. which npm

npm常用命令

  1. npm init
  2. npm install
  3. npm -h
  4. npm -l
  5. npm list -g --depth=0 // 查看全局安装模块

查看 npm全局安装包

  1. 开发中,会有意无意的安装了不少全局安装包,有些可能不会再使用了,有些已经太旧
  2. 查看自己电脑安装过的全局工具包,顺便卸载不需要的

npx 是个不错的选择

推荐使用 npx 的方式代替 npm install -g、yarn global 全局安装

  1. 有些包我们只会使用一次,或者只想尝试以下,不想安装到全局,也不想作为当前项目的依赖
  2. 用 npx 的方式来执行
  3. npx 是 npm 5.2+ 版本之后自带的工具,能够帮助我们更高效的执行 npm 软件仓库里的安装包 ```bash

    npx 之前

    node ./node_modules/.bin/mocha

使用 npx

npx mocha

npx创建 react项目

npx create-react-app my-app

  1. <a name="Uj4XO"></a>
  2. ### npx原理
  3. 执行npx命令,会按以下顺序工作:
  4. 1. 先查看当前项目有没 create-react-app
  5. 1. 如果当前项目找不到,会去全局查找 create-react-app
  6. 1. 如果全局还找不到,会帮我们临时从 npm 包仓库安装 create-react-app,不会污染到当前项目,也不会装到全局
  7. <a name="PyeBf"></a>
  8. ### npm查看全局安装包
  9. ```bash
  10. npm list -g --depth=0

image.png

yarn查看全局安装包

  1. yarn global list --depth=0

image.png

npm的3个部分

  1. npm 官网,网址 www.npmjs.com,查询 npm模块的信息
  2. npm registry,https://registry.npmjs.org/ 模块查询下载的服务 API
  3. npm cli,命令行下载工具,通过它来从 registry 下载模块

npm init

  1. 创建一个 package.json;npm init --yes 创建默认值的 package.json
  2. package.json信息:
    1. 当前模块/项目名称、版本、描述、作者
    2. 配置了当前项目依赖的模块及版本
    3. 配置了当前项目是在哪个 git repo 下
    4. 当前模块的主入口文件 main

npm install

  1. —save,-S
    1. 模块安装到 dependencies,运行时依赖的模块
  2. —save-dev,-D
    1. devDependencies,本地开发时依赖的模块,上线不依赖
  3. 安装模块,一定要指定 —save 或者 -S,确保本地模块正确的添加到 package.json
    1. npm install,简写 npm i
    2. 项目中已有 package.json,可以直接 npm intall 安装所有依赖项
    3. 可以把所有的模块全部 tarball 形式从本地上传到服务器,可以保证所有模块代码的绝对一致性
  4. @ 开头,私有包;scope 通常用于管理私有模块
  1. npm install <git repo url>
  2. npm install https://github.com/petkaantonov/bluebird.git // 仓库地址
  3. npm install https://github.com/caolan/async/tarball/v2.3.0
  4. npm install https://github.com/koajs/koa/archive/2.5.3.tar.gz // tar 包地址来安装
  5. // 安装本地的 tar 包
  6. npm install /Users/black/Downloads/request-2.88.1.tar.gz
  7. // 从本地文件夹安装
  8. npm install ../scott/some-module
  9. // 私有包
  10. npm install @lang/lts
  11. // 安装某个范围内的版本
  12. npm install lodash@">=2.0.0 <3.0.0"
  13. npm install @lang/lts@">=2.0.0 <3.0.0"
  14. // 卸载包
  15. npm uninstall jquery -S

npx包执行工具

  1. npx 是 npm 自带的功能,直接执行依赖包里的二进制文件
    1. 包里的二进制文件会被放到 node_modules/.bin 目录下

npm scripts

脚本能力,在 package.json 里的 scripts 里配置的各种任务

  1. npm start
  2. npm run dev
  3. npm run egg:prod
  1. 复杂的 scripts 会带来非常复杂的依赖队列,不好维护,
  2. 建议把每个独立的任务都分拆开进行组合,把复杂的任务独立写入到一个本地的脚本中,比如 task.js
  1. "scripts": {
  2. // 通过 && 分隔,如果 clean:dist 任务失败,则不会执行后面的构建任务
  3. "build:task1": "npm run clean:dist && npm run build:prod"
  4. // 通过 ; 分隔,无论 clean:dist 是否成功,运行后都继续执行后面的构建任务
  5. "build:task2": "npm run clean:dist;npm run build:prod"
  6. // 通过 || 分隔,只有当 clean:dist 失败,才会继续执行后面的构建任务
  7. "build:task3": "npm run clean:dist;npm run build:prod"
  8. "clean:dist": "rimraf ./dist",
  9. "build:prod": "cross-env NODE_ENV=production webpack",
  10. // 对一个命令传配置参数,可以通过 -- --prod
  11. // 比如 npm run compile:prod 相当于执行 node ./r.js --prod
  12. "compile:prod": "npm run compile -- --prod",
  13. "compile": "node ./r.js",
  14. }

npm publish

  1. https://www.npmjs.com/ 注册账号
  2. 本地(或者从 Github 上)创建创建一个空项目,拉到本地
  3. 增加 .gitignore 忽略文件和 README
  4. npm init 生成 package.json
  5. 编写功能代码,增加到目录 /lib
  6. npm install 本地包进行测试
  7. npm publish 发布包
  8. npm install 线上包进行验证
  9. 修改代码发布一个新版本
  1. npm login
  2. npm publish

在包目录下 npm publish

私有包

  1. 搭建自己的 registry
  2. 第三方的 registry
    1. https://github.com/cnpm/cnpmjs.org
    2. cnpm cli https://github.com/cnpm/cnpm
    3. 团队搭建私有 registry,可以参考 cnpmjs.org
    4. Aliyun Registry
  3. 把本地的包代码,改成 @scope/lts
  4. 然后本地 npm registry 切换到这个源,重新 npm login/npm publish 就可以

npm version

  1. v1.2.0 major minor patch,也就是 主版本号.次版本号.修订号
    1. major: breaking changes (做了不兼容的 API 修改)
    2. minor: feature add(向下兼容的功能性新增)
    3. patch: bug fix, docs(向下兼容的问题修正)
  2. 版本管理参考 https://semver.org/

锁定 npm版本

  1. npm shrinkwrap 把包的版本锁住,保证它的代码每一行每一个字节都恒久不变
  2. npm5 package-lock.json
  3. ~ ^ 自动升级较新版本的包
  1. ~ 选择一个最近的小版本依赖包
  2. ~3.9.0 可以匹配到所有的 3.9.x 版本,不会匹配到 3.10.0
  3. ^ 是匹配最新的大版本
  4. ^3.9.0 可以匹配到所有的 3.x.x,但是不会匹配到 4.0.0
  5. >=
  6. *
  7. // 完全不依赖锁包功能
  8. npm config set package-lock false

全包上传,全包回滚,防止版本不一致带来的错误

package-lock.json

package-lock.json 理解为一个详细描述代码版本的快照文件

  1. {
  2. "name": "npm",
  3. "version": "1.0.0",
  4. "lockfileVersion": 1,
  5. "requires": true,
  6. "dependencies": {
  7. "async": {
  8. "version": "2.6.1",
  9. "resolved": "http://registry.npm.taobao.org/async/download/async-2.6.1.tgz",
  10. "integrity": "sha1-skWiPKcZMAROxT+kaqAKPofGphA=",
  11. "requires": {
  12. "lodash": "^4.17.10"
  13. }
  14. },
  15. "lodash": {
  16. "version": "4.17.11",
  17. "resolved": "http://registry.npm.taobao.org/lodash/download/lodash-4.17.11.tgz",
  18. "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40="
  19. }
  20. }
  21. }
  1. version 就是包的准确版本号
  2. resolved 则是一个明确 tar 包地址,唯一不变的
  3. integrity 这个内容 hash 的值,三个就决定了这个包准确身份信息
  4. requires 字段实现,让这些包向下依赖时保持不变

node_modules

  1. npm3 里面,安装策略改成了平铺结构,全部平铺到 node_modules
  2. 每个文件夹都是一个 package(包),每个包都有自己依赖的其他包,每个包也都有自己的名称和版本

实现一个 Node LTS 查看工具

#!/usr/bin/env node

  1. #! 定义当前脚本的执行环境是用 Node 执行

目录

  1. /index.js
  2. /bin/index.js
  3. /lib
  4. /update.js
  5. /query.js
  6. npm i axios cli-color cli-table compare-versions -S

package.json

  1. "main": "index.js",
  2. "bin": { // 安装包,以后像二进制一样来执行
  3. "lts": "bin/lts.js"
  4. }

/index.js

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

/lib/update.js

  1. const axios = require('axios')
  2. const color = require('cli-color')
  3. const terminalLink = require('terminal-link')
  4. const compareVersions = require('compare-versions')
  5. module.exports = async (v) => {
  6. // 拿到所有的 Node 版本
  7. const { data } = await axios
  8. .get('https://nodejs.org/dist/index.json')
  9. // 把目标版本的 LTS 都挑选出来
  10. return data.filter(node => {
  11. const cp = v
  12. ? (compareVersions(node.version, 'v' + v + '.0.0') >= 0)
  13. : true
  14. return node.lts && cp
  15. }).map(it => {
  16. // 踢出去 file 这个字段,其他的全部返回
  17. const { files, ...rest } = it
  18. const doc = color.yellow(terminalLink('API', `https://nodejs.org/dist/${it.version}/docs/api/documentation.html`))
  19. return { ...rest, doc }
  20. })
  21. }

/lib/query.js

  1. const Table = require('cli-table')
  2. function query(dists) {
  3. const keys = Object.keys(dists[0])
  4. // 建立表头
  5. const table = new Table({
  6. head: keys
  7. })
  8. // 拼接出表格的每一行
  9. return dists
  10. .reduce((res, item) => {
  11. table.push(
  12. Object.values(item)
  13. )
  14. return res
  15. }, table)
  16. .toString()
  17. }
  18. module.exports = query

/bin/lts.js

  1. #!/usr/bin/env node
  2. const pkg = require('../package')
  3. // 从顶层 index.js 里面拿到 lib 下面模块暴露的方法
  4. const query = require('..').query
  5. const update = require('..').update
  6. // 输出结果到命令行窗口
  7. function printResult(v) {
  8. update(v).then(dists => {
  9. const results = query(dists, v)
  10. console.log(results)
  11. process.exit()
  12. })
  13. }
  14. function printVersion() {
  15. console.log('ltsn ' + pkg.version)
  16. process.exit()
  17. }
  18. // 一些命令的帮助提示
  19. function printHelp(code) {
  20. const lines = [
  21. '',
  22. ' Usage:',
  23. ' lts [8]',
  24. '',
  25. ' Options:',
  26. ' -v, --version print the version of vc',
  27. ' -h, --help display this message',
  28. '',
  29. ' Examples:',
  30. ' $ lts 8',
  31. ''
  32. ]
  33. console.log(lines.join('\n'))
  34. process.exit(code || 0)
  35. }
  36. // 包的入口函数,里面对参数做剪裁处理,拿到入参并给予
  37. // 不同入参的处理逻辑
  38. function main(argv) {
  39. if (!argv) {
  40. printHelp(1)
  41. }
  42. const getArg = function() {
  43. let args = argv.shift()
  44. args = args.split('=')
  45. if (args.length > 1) {
  46. argv.unshift(args.slice(1).join('='))
  47. }
  48. return args[0]
  49. }
  50. let arg
  51. while (argv.length) {
  52. arg = getArg()
  53. switch(arg) {
  54. case '-v':
  55. case '-V':
  56. case '--version':
  57. printVersion()
  58. break
  59. case '-h':
  60. case '-H':
  61. case '--help':
  62. printHelp()
  63. break
  64. default:
  65. printResult(arg)
  66. break
  67. }
  68. }
  69. }
  70. // 启动程序就开始执行主函数
  71. main(process.argv.slice(2))
  72. module.exports = main

npm无法安装

  1. npm config get proxy
  2. npm config get https-proxy
  3. 如果返回值不为null,继续执行
  4. npm config set proxy null
  5. npm config set https-proxy null

npm run mock & npm run dev 无法同时运行的解决

  1. 运行mock服务器和项目的命令,只能启动第一个
  2. 解决方法:
    1. 开2个命令窗口,先运行 npm run mock,后运行 npm run dev
      1. npm run mock && npm run dev // 无效
      2. npm run mock | npm run dev // 无效

npm config

配置成功
npm config ls -l

设置 npm源

  1. npm config set registry http://registry.npm.taobao.org/
  2. # 验证是否成功
  3. npm config get registry
  4. # 还原为 npm源
  5. npm config set registry https://registry.npmjs.org/
  6. # 使用 npm而不是默认的 yarn
  7. --use-npm

运行多个命令

  1. npm install concurrently --save-dev
  2. 使用
  3. "script": {
  4. "start": "concurrently \"react-scripts start\" \"npm run mock\""
  5. },
  6. "proxy": "http://localhost:3030" // 代理异步请求

检查 npm 包是否被注入恶意代码

  1. flatmap-stream@0.1.1 代表项目有影响
    1. npm ls event-stream flatmap
  1. Use the `--scripts-prepend-node-path` option to include the path for the node binary npm was executed with.
  2. npm config set scripts-prepend-node-path true // 无效