npm命令解析

npm i -D unplugin-auto-import

npm i : npm install 的简写,安装包 -D : --save-dev 的简写,装到开发依赖 (devDependencies) unplugin-auto-import: 包名,就是 Vite 里那个自动导入插件

为什么用 -D 而不是直接 npm i 因为这类插件只在开发 / 构建阶段 起作用(Vite 编译时扫描代码、补 import),跑起来的网页本身不依赖它。所以放 devDependencies:生产部署时 npm install --production 不会装它,包更小。像 Vue、Element Plus 这种运行时要用的是 dependencies

package.json 里的分工:

json 复制代码
"dependencies": {          // 运行时需要
  "vue": "^3.5.40",
  "element-plus": "^2.14.3"
},
"devDependencies": {       // 只在开发/构建时用
  "unplugin-auto-import": "^21.1.0",
  "unplugin-vue-components": "^32.1.0",
  "vite": "^8.1.5"
}

装 "构建工具" 用 -D,装 "运行时库" 不用 -D

详细步骤

npm i -D unplugin-auto-import 执行时:

1. 读本地(只加载,不判定) 读 package.json(已有依赖)、lock(虚拟树)、node_modules(实际树)

2. 构建理想树

  └─先根据package.json建立理想树

  ├─ 已有边:查 lock 候选 → 满足 → 照抄 lock 版本;不满足/没有 → 问 registry 取范围内最高版   ├─ 新边 unplugin-auto-import:lock 里没有 → 问 registry → latest = 21.1.0 → 挂入树

  ├─ 递归它的 dependencies(unimport、unplugin...):每条边同上判定  

  └─没有 lock 时:以 node_modules 为起点建树,并进行后续的校验

理想树 的边就是依赖 不带版本号表示下载最新版本 ,下载最新版本的时候不查 lock/本地

3. diff: 获取操作清单 对比理想树和实际树(node_modules),给出 "该装什么、该删什么、该换什么" 的完整操作清单

关于node_modules的校验 :即使在建树阶段校验过了,这里还要校验了,因为建树阶段的校验是为了知道"这条边用什么版本 ",而diff的校验是为了获得操作清单

4. 实际化(reify):按树下载 照着diff给出的清单执行 → 下载解压、删除孤儿、替换版本

下载好的依赖解压进node_modules 即使我运行 npm i <包名> 的目的只是想要安装 <包名> ,npm依旧按照清单下载解压所有的包

5. 保存(全部成功后才写) 先写 package.json(save-prefix(^) + 21.1.0 → "^21.1.0")→ 最后写 lock(21.1.0 + 整棵树)

关于理想树

理想树是我们想要得到的依赖结构。

命令运行结束后,理想树会写进 package-lock.json

我们将package-lock.json称为虚拟树 ,node_modules称为实际树

建树时使用 package-lock.json 是因为 package-lock.json 是上一次npm命令运行之后的产物,通过直接调用上一次运行后的结果来建树会更加的快

关于npm install

  1. npm i unplugin-auto-import没写版本 = 默认要 latest 标签, 问 registry 要 latest,包 manifest 里有 dist-tags: { latest: "21.1.0" }

    registry 是 npm 的 "中央仓库服务器",存放着所有公开 npm 包的地方。 官方地址:registry.npmjs.org 使用命令行npm config ls -l可以查看本机的具体配置 manifest 是你要装的那个包(unplugin-auto-import)在 registry 上的 "登记档案"

  2. 如果直接执行npm install,会把package.jsonpackage-lock.json里面的依赖全部装齐

    package.json里面是范围,lock 里面是具体版本 如果package里面有而lock里面没有,下载package范围内的最高版本 如果package里面有且lock里面也有,下载lock的版本 如果package里面没有但lock里面有, 如果这个依赖是某个已声明依赖的传递依赖,保留,如果不是,则清除 (传递依赖会随着主依赖的下载而顺带着下载)

  3. npm i <包名>(不带版本), = 默认要 latest 标签,即使本地已经下载了最新版本依然会问register询问

  4. npm i <包名>@<版本>(指定版本),如果本地已经有了就不在询问register

  5. 如果下载没有完全下载好就失败了,那就不会修改package和lock,但是第二次继续下载的时候第一次下载好的包不会重新下载

  6. 在下载依赖的时候,如果该依赖所需的传递依赖本地已经有了,就不会再下载

下载中断后,第二次各包怎么处理

  1. 已完整下载的包: 直接从npm 的本地缓存解压

  2. 下载到一半失败的包:缓存没有合法条目,必须重新下载

  3. 已解压进 node_modules 的包:实际树里有它,重试时 diff 算"已有",连解压都省了

    diffreify(动手装) 之前的第一步:Diff.calculate({ actual, ideal }) 把 node_modules 现状和理想树逐节点对比,产出一张 "ADD / REMOVE / CHANGE / 不动" 的操作清单,_reifyPackages 再照着清单执行。 它是 npm 的 "决策层",负责回答一个关键问题:到底哪些事值得做。

为什么缓存能 "认出" 要用的文件

npm 的缓存是内容寻址 的:每个下载完成的 tarball 按它的哈希值(integrity)存进 _cacache。而 lock 里恰好记录着每个包的 integrity 字段 ------lock 里的 integrity 就是找缓存的钥匙

json 复制代码
"node_modules/left-pad": {
  "version": "1.0.0",
  "integrity": "sha512-Xvly4CWfzT7TGZRB2Qd7ub2dmJDomZCNf3BuT3XXfrNerQHtta82NNhbvToU4Qiz7IHrgY...",
  "resolved": "https://registry.npmmirror.com/left-pad/-/left-pad-1.0.0.tgz"
}

下载时先算哈希,对得上才写进缓存;第二次要装时按同一个哈希查缓存,查到就直接用。

npm i <包名>@<版本> --offline

--offline 表示完全不联网,包括查询 registry 也不行。

--prefer-offline 表示缓存优先。如果缓存命中了,即使缓存过期了,也不查询registry

默认 什么都不加的话也是缓存优先,但如果缓存过期了的话,会查询registry验证一次

相关推荐
咸鱼老弟1 小时前
AI Agent 的"自主性悖论"——为什么给它的自由度越大,越要配一套更硬的护栏
前端·人工智能
默_笙2 小时前
💫 闭包是个背包:拆解小米前端面试题里的三道"闭包陷阱"
前端·javascript·面试
天若有情6732 小时前
粒子星空背景单页HTML模板|炫酷动态星空特效网页(纯前端)
前端·html·css动画·网页特效·粒子星空·静态网页模版
工业涂料百问2 小时前
【趋势前沿】系列(三)工业涂料国产替代的三个台阶:航空、海工、电子的突破路径与认证壁垒
前端
前端snow2 小时前
ai agent --- agentic RAG
前端
gyratesky2 小时前
记录一种很新的大屏开发方式
前端·数据可视化
xy34533 小时前
Axure 9.0 中继器核心结构
前端·ui·html·axure·原型·产品设计
老王以为3 小时前
走进 AI Agent 第四篇(上):知识获取管道——RAG 基础
前端·人工智能·全栈