🗒️配置 npm
1. 下载和安装
要从 public npm registry 安装和发布 packages,就必须安装 Node.js 和 npm 命令行工具。强烈建议使用 Node version manager 来安装 Node.js and npm,而不建议用 Node installer(因为它会把 npm 安装在一个具有 local 权限的目录里,这会导致 permissions errors 当我们全局运行 npm 包时)。
Node version manager:
Windows
nodist
nvm-windows
node -v
npm -v2. 目录结构
介绍 npm 使用的 folder structures。
npm 会在我们的电脑上放各种东西。当然,这正是它的工作。
2.1 不同模式
local 安装(默认):会安装在
./node_modulesglobal 安装(
-g):会安装在prefix配置的文件夹下prefix配置默认是 node 安装的位置大多数系统,
/usr/localWindows,
%AppData%\npmUnix,它是上一级(one level up),因为 node 通常安装在
{prefix}/bin/node而不是{prefix}/node.exe
若
prefix没设置,则就用当前 package 或者当前工作目录的根目录
若想 require() 使用,就 local 安装。若想在 command line 里使用,就 global 安装。若想同时使用,就在两个地方都安装,或者使用 npm link。
2.2 安装位置
packages 或
node modules
./node_modules
Unix 系统
{prefix}/lib/node_modules/Windows 系统
{prefix}/node_modules/
可执行文件 be linked to
./node_modules/.bin/
Unix 系统
{prefix}/bin/Windows 系统
{prefix}/
操作手册 be linked to
不安装
{prefix}/share/man
cache 文件
Posix
~/.npmWindows
%AppData%/npm-cache
临时文件
默认是环境变量 TMPDIR, TMP, TEMP
Unix
/tmpWindows
c:\windows\temp
补充说明:
packages
scoped packages 的安装位置相同,只是它们又被分进了子目录
若是 local 安装的
可以使用
require("packagename")去 load 它的 main module使用
require("packagename/lib/path/to/sub/module")去 load 其它 modules
可执行文件
对于 local 安装的,就能通过 npm run script 了
cache 文件
其路径可以由
cacheconfig 参数控制的详见
npm cache
临时文件
其路径可以由
tmpconfig 参数配置控制每次 run program 时,临时文件都会在此根目录下指定一个唯一的文件夹,并在成功退出时被删除
2.3 prefix 补充
prefix 补充当 local 安装时,npm 首先尝试找到合适的 prefix folder。这样的话,即便我们碰巧 cd 进入了其它目录,npm install xxx 也会安装到合理的根目录中。
从
$PWD开始,npm 将遍历 folder tree 以检查包含package.json文件或node_modules文件夹的 folder。如果找到了,那它就被视为有效的 current directory,以运行 npm commands
这个逻辑受 git 的
.git-folder的查找逻辑的启发。
如果没有找到 package root,则使用 current folder。
当运行
npm install foo@1.2.3时package 被 loaded 到 cache 中,然后 unpacked 到
./node_modules/foo。接着,所有 foo 的依赖都被类似地 unpacked 到
./node_modules/foo/node_modules/..
所有 bin 文件都 symlinked(符号链接)到
./node_modules/.bin/以便在运行 npm scripts 时可以找到它们。
对于 global 安装,packages 的安装方式大致相同,只是使用的文件夹不同。
2.4 循环+冲突+文件夹优化
cycles, conflicts 和 folder parsimony(简约)
cycles 是使用 node 的 module system 的属性来处理的,它会在目录中查找 node_modules 文件夹。所以,在每个阶段,如果某个 package 已经安装在了其祖先的 node_modules 文件夹中,那么它就不在当前位置安装了。
比如 foo -> bar -> baz,如果再加一个 baz -> bar,就不用将 bar 的另一个副本再放到 .../baz/node_modules/ 里了,因为当 baz 调用 require("bar") 时会从 foo/node_modules/bar 里获得安装的副本。
这种 shortcut(快捷方式)只使用在多个嵌套的 node_modules 文件夹中需要安装 exact same version 的 package 时。当 a 的版本不一样时还是有可能出现 a/node_modules/b/node_modules/a 的。然而,如果不多次重复 exact same package,将始终 prevent(阻止/避免)无限 regress(倒退)。
还可以做个优化,就是在尽可能高的级别下安装 dependencies,在 localized "target" folder (hoisting, 提升)。从版本 3 开始,npm 默认 hoist(提升)dependencies。
比如如下 dependency graph:
第一:先处理第一层的依赖,即 foo 的依赖项
如下:
第二:再依次处理第二层的依赖
对于
blerg@1.2.5,它没有依赖项。可以考虑 hoist 了,但因为其直接父已经是 root 了所以忽略对于
bar@1.2.3,它有三个依赖项blerg@1.x不用安了,因为其祖先已经有了,且版本匹配baz@2.x需要安,因为它和祖先的baz@1.2.3版本不匹配asdf@*需要安
对于
baz@1.2.3,它有一个依赖项quux@3.x需要安
结果如下:
第三:再依次处理第三次依赖
bar@1.2.3 -> baz@2.x,它有一个依赖quux@3.x不用安,因为祖先里已经有了此时
bar@1.2.3 -> baz@2.x是个叶子节点,但因为 root 下已经有另外一个版本的了所以不提升
bar@1.2.3 -> asdf@*,它没有依赖。可以考虑 hoist,将其提升到 rootbaz@1.2.3 -> quux@3.x,它有一个依赖bar@1.2.3不用安,因为祖先里已经有了此时
baz@1.2.3 -> quux@3.x是个叶子节点,所以提升到 root
结果如下:
所以,最终的 folder structure 如上,我们把所有 dependencies 都 hoist 到尽可能高的级别。当然,真实项目里的版本都是符合条件的明确版本。
我们可以使用 npm ls 命令查看安装的 graphical breakdown(细分)。
2.5 发布
一旦发布,npm 将会在 node_modules 文件夹中查找。只要是不在 bundleDependencies 数组中的 items,就不会被包含在 package tarball(压缩包)中。
这允许 package 维护者在本地安装他们所有的 dependencies(和 dev dependencies),但只 re-publish 那些在别处找不到的 items。相关详情可查阅 package.json。
3. .npmrc
.npmrcnpm 配置文件。npm 从 command line, environment variables 和 npmrc files 获取它的 config settings。
四个相关文件是:
每个项目的配置文件
/path/to/my/project/.npmrc每个用户的配置文件
~/.npmrc全局配置文件
$PREFIX/etc/npmrcnpm 内置配置文件
/path/to/npm/npmrc
npm config 命令可用来更新和编辑 user 和 global npmrc files 的内容。
所有的 npm 配置文件都是 ini 格式的 key = value 参数对列表。注释是以 ; 或 # 开头的行。
4. package.json
package.jsonpackage.json 必须是实际的 JSON,而不仅仅是一个 JavaScript 对象字面量。
更多信息详见 npm CLI / Configuring npm / package.json。
这里介绍的许多行为都受 npm CLI / Using npm / Config 配置设置的影响。
5. lockfile
5.1 package-lock.json
package-lock.json当 npm 修改 node_modules 树或修改 package.json 时,就会自动生成 package-lock.json。package-lock.json 描述了生成的 exact tree,以便后续的 install 能生成相同的树,而不管中间 dependency 的更新。
5.1.1 应提交 repo
package-lock.json 应该提交到 source repository,目的有:
确保后续的 install 能生成完全相同的依赖关系
不用提交
node_modules目录本身提高 tree changes 的可见性,通过可读的 source control diffs
优化安装过程,因为 npm 可以跳过重复的 metadata resolution
从 npm v7 开始,lockfiles 包含了足够的信息以获取 package tree 的完整 picture,减少读取
package.json文件的需要,允许显著的性能提升
5.1.2 隐藏的 lockfiles
为了避免重复处理 node_modules 文件夹,从 npm v7 开始使用一个 hidden lockfile node_modules/.package-lock.json。它包含 tree 的相关信息,用于代替读取整个 node_modules hierarchy(层次结构),前提是满足以下条件:
它引用的所有 package folders 都存在于
node_modules层次结构中不存在 package folders 是在
node_modules层次结构中却没在 lockfile 中列出file 的修改时间至少和其引用的所有 package folders 一样新
npm 的旧版本会忽略该 hidden lockfile。
5.2 npm-shrinkwrap.json
npm-shrinkwrap.json可发布的 lockfile(锁文件)。
该文件是由 npm shrinkwrap 命令创建的。它等价于 package-lock.json,不同的是 npm-shrinkwrap.json 可能会在发布 package 时包含。
5.3 对比
package-lock.json vs npm-shrinkwrap.json
相同点:格式相同,在项目的根目录下发挥着相似的功能
不同点:
package-lock.json不能发布,并且在项目的根目录以外的任何地方该文件都会被忽略npm-shrinkwrap.json允许发布,并且从 point 定义 dependency tree。除非部署 CLI 工具或以其它方式使用发布过程来生成 production packages,否则不建议这样做
如果 package 根目录中同时有这两个文件,那么 npm-shrinkwrap.json 会优先于 package-lock.json 文件。
Last updated