npm i -D unplugin-auto-import
npm i:npm install的简写,用于安装依赖包-D:--save-dev的简写,装到开发依赖(devDependencies)unplugin-auto-import: 包名,就是 Vite 里那个自动导入插件
为什么用-D而不是直接npm i?
因为这类插件只在开发 / 构建阶段起作用,跑起来的网页本身不依赖它。所以放 devDependencies。
部署生产环境执行npm install --production时,会跳过安装这个包,缩减部署包大小。 像 Vue、Element Plus 这种页面实际运行时要加载的库,才放在dependencies生产依赖里。
npm install --production: 安装依赖时,跳过devDependencies(开发依赖),只安装dependencies(生产依赖)
package.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 为起点建树,并进行后续的校验
理想树的边就是依赖
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为基础,一方面是因为它锁定了历史版本,能保证跨环境构建结果一致;另一方面是直接复用上次的解析结果,可以大幅提升安装速度。
关于npm install
npm i <包名>(不带版本)= 默认要 latest 标签。
此时如果这个包已经存在于 lock 文件中,会优先参考 lock 里的版本,只有当 lock 版本不满足 package.json 范围时才去 registry 拉最新
问registry要 latest,包manifest里有dist-tags: { latest: "21.1.0" }registry是 npm 的 “中央仓库服务器”,存放着所有公开 npm 包的地方。
官方地址:https://registry.npmjs.org
使用命令行npm config ls -l可以查看本机的具体配置
manifest是你要装的那个包(unplugin-auto-import)在 registry 上的 “登记档案”如果直接执行
npm install,会把package.json和package-lock.json里面的依赖全部装齐package.json里面是范围,lock 里面是具体版本
如果package里面有而lock里面没有,下载package范围内的最高版本
如果package里面有且lock里面也有,下载lock的版本
如果package里面没有但lock里面有,
如果这个依赖是某个已声明依赖的传递依赖,保留,如果不是,则清除
(传递依赖会随着主依赖的下载而顺带着下载)npm i <包名>@<版本>(指定版本),如果 lock中已经存在该版本,就不会再向 registry 查询如果下载没有完全下载好就失败了,那就不会修改package和lock,但是第二次继续下载的时候第一次下载好的包不会重新下载
在下载依赖的时候,如果该依赖所需的传递依赖本地已经有了,就不会再下载
下载中断后,第二次各包怎么处理
已完整下载的包: 直接从npm 的本地缓存解压
下载到一半失败的包:缓存没有合法条目,必须重新下载
已解压进 node_modules 的包:实际树里有它,重试时 diff 算"已有",连解压都省了
diff是reify(动手装)之前的第一步:
Diff.calculate({ actual, ideal })把 node_modules 现状和理想树逐节点对比,产出一张 “ADD / REMOVE / CHANGE / 不动” 的操作清单,_reifyPackages再照着清单执行。 它是 npm 的 “决策层”,负责回答一个关键问题:到底哪些事值得做。
为什么缓存能 “认出” 要用的文件
npm 的缓存是内容寻址的:每个下载完成的tarball按它的哈希值(integrity)存进_cacache。而 lock 里恰好记录着每个包的 integrity 字段 ——lock 里的 integrity 就是找缓存的钥匙:
"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验证一次