通俗易懂的 npm 全面总结

1,355 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

大家好,我是小十七_,下面是通俗易懂的 npm 全面总结,希望能帮助到大家查漏补缺~

内容参考自这个视频,可以结合一起看:www.youtube.com/watch?v=P3a…

npm 是什么

npm 的英文是,node package manager,是 node 的包管理工具

为什么需要 npm

就像建造汽车一样,如果发动机、车身、轮胎、玻璃等等都自己做的话,几十年也做不完。但是如果有不同的厂商,已经帮我们把各个零件都制作好,我们只负责组装,那整个过程会运作的非常快

同样的道理,如果软件的逻辑,我们都自己开发的话,写一个功能需要非常长的时间。如果使用已有的功能、工具、npm 包,快速的组装我们想要的功能,不是让事情变得非常简单吗

npm 安装的包为什么可以被引入

我们使用比如 npm install chalk 安装包时,会被安装到本地 node_modules 目录下

在实际页面 import 时,根据 node 的模块系统的实现方式,会按照这个判断:是否是核心模块(fs 等),是否是相对路径,package.json 同级目录是否有 node_modules,上级目录是否有,一直到文件系统的根目录下是否有 node_modules 文件夹。

但是这样找到的一般是文件夹,如果文件夹目录下有 package.json 文件,会读取里面的 main 字段指向的文件,如果没有,会读取 index.js 文件。

webpack 等打包工具是兼容 node 模块系统的,于是也会使用这种方式寻找模块,同时会做一些优化,比如支持配置 alias 和 external,自定义 import 的逻辑。

alias: {
  Icon: path.resolve(__dirname, 'src/components/Icon.jsx'),
}
import Icon from 'Icon' // -> /path/to/src/components/Icon.jsx

npm 全局安装的位置

npm install -g @tarojs/cli

stackoverflow.com/questions/2…

那么 taro 全局安装到什么位置了呢?如果使用了 nvm,则放到了 nvm 对应版本的文件夹下

➜  ~ npm root -g
/Users/abc/.nvm/versions/node/v14.15.0/lib/node_modules

npm root -g

npm root 的作用是打印出当前工作目录下,有效的 node_modules 文件夹的位置,如果直接执行这个命令:

➜  ~ npm root
/Users/abc/node_modules

➜  ~ cd testtest git:(dev) ✗ npm root
/Users/abc/test/node_modules

npm root -g 打印出的就是全局安装的 node_modules 位置

全局安装的位置

需要看是否用了 nvm:

  • 没有用 nvm 的情况:
➜  ~ cd /usr/local/lib/node_modules
➜  node_modules ls
anywhere  npm       npm-check
  • 用了 nvm 的情况,放到 nvm 对应版本的文件夹下 ~/.nvm/versions/node/{version}/lib/node_modules/
➜  ~ cd ~/.nvm/versions/node/v14.15.0/lib/node_modules
➜  node_modules git:(3d9c31d) ls
@ies    @tarojs npm     pnpm    typeorm

which npm

➜  ~ which npm
/Users/abc/.nvm/versions/node/v14.15.0/bin/npm

which 是一个 linux 命令,在环境变量 $PATH 中查找命令对应的可执行文件

npm 安装包的结构

是平铺的结构

比如安装 react 和 react-dom 安装完成后,会发现除了它们,还有其他几个包

这是因为它们是,react、react-dom 的依赖,会被扁平的安装到 node_modules 中,可以通过 npm list 看到

➜  src git:(master) ✗ npm list
react_basic_app@0.0.1 /Users/abc/react_basic_app
├─┬ react@17.0.2
│ ├─┬ loose-envify@1.4.0
│ │ └── js-tokens@4.0.0
│ └── object-assign@4.1.1
└─┬ react-dom@17.0.2
  ├── loose-envify@1.4.0 deduped
  ├── object-assign@4.1.1 deduped
  └─┬ scheduler@0.20.2
    ├── loose-envify@1.4.0 deduped
    └── object-assign@4.1.1 deduped

另外,可以通过增加参数的方式,查看具体包的依赖情况

➜  src git:(master) ✗ npm ls object-assign
react_basic_app@0.0.1 /Users/abc/react_basic_app
├─┬ react@17.0.2
│ └── object-assign@4.1.1
└─┬ react-dom@17.0.2
  ├── object-assign@4.1.1  deduped
  └─┬ scheduler@0.20.2
    └── object-assign@4.1.1  deduped

可以发现有好几个包都依赖了 object-assign,但是 react-dom 中的两个写了 deduped,并且实际在 node_modules 中 object-assign 只安装了一份

deduped 是 deduplicated 的缩写,意思是重复项被删除

npm 会通过搜索本地包的树状结构,通过将包之间的依赖关系向上移动,来简化整体的结构,这样依赖可以在多个包之间共享了

于是,object-assign 可以只安装一份,是因为 npm 简化了他们间的依赖关系

npm 包的版本规则

比如 4.1.1

  1. 第一个是主要版本,代码有很大变动,不能向后兼容的修改

  2. 第二个是次要版本,可以向后兼容,升级后不会造成影响的修改

  3. 第三个是补丁版本,修改小bug,同样不会造成影响的修改

在 package 中,可以看到安装完包之后,版本前面有个符号,比如

"dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  }

^ 表示更新次要版本和补丁,不会更新主要版本,当你运行 npm update 的时候,它会自动更新。

如果想要更新主要版本,可以运行 npm i react@latest。安装特定大版本的话 npm i react@16 就会安装 react 16 版本。安装特定次要版本的话 npm i react@~16.2

package-lock.json

当和其他人协作时,如果新 clone 一个项目,只看 package.json 的话,不同人安装的 次要版本、补丁版本 可能是不同的,如果某个版本的包有什么问题,项目就可能跑不起来

package-lock.json 作用是规定安装包的特定版本,npm 会根据 package-lock.json 中的特定版本安装依赖包,保证你安装的版本和别人的相同。