install npm 到某个文件下执行_你可能不知道的 npm 依赖管理那些事

a3a71ee6496f78f64b806c1895e8793c.png

点击上方蓝字关注我们

npm 是 Node.js 默认的、以 JavaScript 编写的包管理工具,如今,它已经成为世界上最大的包管理工具,是每个前端开发者必备的工具。不知你是否遇到过下面问题:

哎?我本地明明是好的,线上的依赖怎么就报错不行了呢?一言不合就删除整个node_modules目录然后重新npm install

今天我们聊聊npm模块相关的东西。

semver

npm 依赖管理的一个重要特性是采用了语义化版本 (semver) 规范,作为依赖版本管理方案。

semver规定的模块版本号格式为:MAJOR.MINOR.PATCH,即主版本号.次版本号.修订号。版本号递增规则如下:

  1. 主版本号:当你做了不兼容的 API 修改,例如新增了breaking change。

  2. 次版本号:当你做了向下兼容的功能性新增,例如新增feature。

  3. 修订号:当你做了向下兼容的问题,例如修复bug。

对于npm包的引用者来说,经常会在package.json文件里面看到使用semver约定的semver range来指定所需的依赖包版本号和版本范围。常用的规则如下表:

6f4e97e4ed1719b275a2ef3ebd66851d.png

此外,任意两条规则,用空格连接起来,表示“与”逻辑,即两条规则的交集: 如 >=2.3.1 <=2.8.0 可以解读为: >=2.3.1 且 <=2.8.0

任意两条规则,通过 || 连接起来,表示“或”逻辑,即两条规则的并集: 如 ^2 >=2.3.1 || ^3 >3.2

在修订版本号的后面可以加上其他信息,用-连接,比如:

  • X.Y.Z-Alpha: 内测版

  • X.Y.Z-Beta: 公测版

  • X.Y.Z-Stable: 稳定版

从 npm install 说起

npm install 命令用来安装模块到 node_modules 目录。npm install 的具体原理是什么呢?

执行工程自身 preinstall

确定首层依赖模块

首层依赖是 package.json 中 dependencies 和 devDependencies 字段直接指定的模块。每一个首层依赖模块都是模块依赖树根节点下面的一颗子树。

获取模块

获取模块是一个递归的过程,分为以下几步:

  1. 获取模块信息。在下载一个模块之前,首先要确定其版本,这是因为 package.json 中的模块版本往往是 semantic version。此时根据package.json和版本描述文件(npm-shrinkwrap.json 或 package-lock.json不同npm版本的策略不同,后续我们会详细介绍)。如 package.json 中某个包的版本是 ^1.1.0,npm 就会去仓库中获取符合1.x.x形式的最新版本。

  2. 获取模块实体。上一步会获取到模块的压缩包地址(resolved 字段),npm 会用此地址检查本地缓存,缓存中有就直接拿,如果没有则从仓库下载。

  3. 查找该模块依赖,如果有依赖则回到第1步,如果没有则停止。

模块扁平化 (npm3 后支持)

上一步获取到的是一颗完整的依赖树,下面会根据依赖树安装模块。模块安装机制有两种:嵌套式安装机制 和 扁平式安装机制。

例如某工程下直接依赖了A和B两个包,且他们同时依赖了C包。

  • 嵌套式

57e9364be0a6acc9b8172f5b6fd22fde.png

npm3之前使用的是嵌套式安装机制,严格按照依赖树的结构进行安装,这可能会造成相同模块大量冗余的问题。

  • 扁平式

3944923c4b938d3be4bbdbf797a71bcd.png

npm3之后使用的扁平式安装机制,但是需要考虑一个问题:

工程同时依赖一个模块不同版本该如何解决?

npm3 引入了 dedupe 过程来解决这个问题。它会遍历所有节点,逐个将模块放在根节点下面,也就是 node-modules 的第一层。当发现有重复模块时,则将其丢弃。

重复模块:semver兼容的相同模块。例如lodash ^1.2.0lodash ^1.4.0。如果工程的两个模块版本范围存在交集,就可以得到一个 兼容版本,不必版本号完全一致,这可以使得更多冗余模块在dedupe过程中被去掉。

上例中如果A包依赖C@1.0.0,B包依赖C@2.0.0,此时两个版本并不兼容,则后面的版本仍会保留在依赖书中。如下图所示:

24f9cf736d75e06ca48331c78183f8bc.png

实际上,npm3仍然可能出现模块冗余的情况,如下图,因为一级目录下已经有C@1.0.0,所以所有的C@2.0.0只能作为二级依赖模块被安装:

64bb63a83b0fbf3ad66131873b7744b4.png

npm提供了 npm dedupe 指令来优化依赖树结构。这个命令会去搜索本地的node_modules中的包,并且通过移动相同的依赖包到外层目录去尽量简化这种依赖树的结构,让公用包更加有效被引用。

安装模块

将会更新工程中的 node_modules,并执行模块中的生命周期函数(按照 preinstall、install、postinstall 的顺序)

执行工程自身生命周期

当前 npm 工程如果定义了钩子此时会被执行(按照 install、postinstall、prepublish、prepare 的顺序)。最后生成或者更新版本描述文件。

锁定npm依赖版本
你是否遇到过本地开发时一切正常,发布线上代码时因为安装依赖的错误导致服务不可用?如果是的话,你要一份版本描述文件。

简单的写死当前工程依赖模块的版本并不能真正锁定依赖版本,因为你无法控制间接依赖,如果间接依赖更新了有问题的模块,你的系统还是可能会有宕机的风险。

lock 文件是当前依赖关系树的快照,允许不同机器间的重复构建。其实 npm5 之前已经提供了lock文件—— npm-shrinkwrap.json。但是在 npm5 发布的时候创建了新的lock文件—— package-lock.json,其主要目的是希望能更好的传达一个消息,npm真正支持了locking机制。不过二者还是有一些区别点:

  1.  发布npm包时,package-lock.json 不会被发布, 即使你将其显式添加到软件包的 files 属性中,它也不会是已发布软件包的一部分。npm-shrinkwrap.json 可以被发布。

  2. npm-shrinkwrap.json向后兼容npm2、3、4版本,package-lock.json 只有 npm5 以上支持。

  3. 可以通过npm shrinkwrap命令将package-lock.json转换成npm-shrinkwrap.json, 因为文件的格式是完全一样的。

曲折的package-lock.json

查阅资料得知,自npm 5.0版本发布以来,package-lock.json的规则发生了三次变化。

  1. npm 5.0.x版本,不管 package.json 怎么变,npm install都会根据lock文件下载。npm/npm#16866 控诉了这个问题,我明明手动改了 package.json ,为啥不给我升包!然后就导致5.1.0的问题(是个bug)

  2. npm 5.1.0 - 5.4.1版本,npm insall会无视lock文件,去下载semver兼容的最新的包。导致lock文件并不能完全锁住依赖树。详情见npm/npm#17979

  3. npm 5.4.2版本之后,如果手动改了package.json,且package.json和lock文件不同,那么执行npm install时 npm 会根据 package 中的版本号和语义含义去下载最新的包,并更新至 lock。

    如果两者是同一状态,那么执行 npm install都会根据 lock 下载,不会理会 package 实际包的版本是否更新。

好的依赖管理方案

  • 使用 npm: >=5.4.2 版本, 保持 package-lock.json 文件默认开启配置

  • 初始化:第一作者初始化项目时使用 npm install  安装依赖包, 默认保存 ^X.Y.Z 依赖 range 到 package.json 中; 提交 package.json, package-lock.json, 不要提交 node_modules 目录

  • 初始化:项目成员首次 checkout/clone 项目代码后,执行一次 npm install 安装依赖包

  • 升级依赖包:

    • 升级小版本: 本地执行 npm update 升级到新的小版本

    • 升级大版本: 本地执行 

npm install @
  • 升级到新的大版本

  • 也可手动修改 package.json 中版本号为要升级的版本(大于现有版本号)并指定所需的 semver, 然后执行 npm install

  • 本地验证升级后新版本无问题后,提交新的 package.json, package-lock.json 文件

降级依赖包:

正确: 

 npm install @

验证无问题后,提交 package.json 和 package-lock.json 文件

删除依赖包:

  • Plan A: 

npm uninstall 

    并提交 package.json 和 package-lock.json

  • Plan B: 把要卸载的包从 package.json 中 dependencies 字段删除, 然后执行 npm install 并提交 package.json 和 package-lock.json

任何时候有人提交了 package.json, package-lock.json 更新后,团队其他成员应在 svn update/git pull 拉取更新后执行 npm install 脚本安装更新后的依赖包

不要手动修改 package-lock.json

当 package-lock.json 出现冲突时,这种是非常棘手的情况,最好不要手动解决冲突,如果有一处冲突解决不正确可能会导致线上事故。
建议的做法:将本地的 package-lock.json文件删除,引入远程的 package-lock.json 文件,再执行npm install命令更新package-lock.json文件。

(这种做法能保证未修改的依赖不变,会存在一个风险:在执行npm install的时候,可能有些间接依赖包升级,根据semver兼容原则导致本次安装的和开发时的package-lock.json文件不同。这种情况就需要验证依赖包升级是否有影响)

部署安装依赖时,执行npm install命令。不要执行

npm install

命令,因为这会导致 package-lock.json 文件同时被更新。

问题来了

上述最佳实践提到了当团队中有成员提交了 package.json, package-lock.json 更新后,其他成员需要执行 npm install 来保证本地依赖的及时性,那么能否写一个插件将这个手动的环节自动化呢?答案是可以的,我们只需要在 git post-merge 钩子中检查
git diff files(git diff-tree -r --name-only --no-commit-id HEAD@{1} HEAD)

是否包含了 package.json 文件,如果包含了该文件,则执行npm install命令。我们暂且给这个插件取名为 hawkeye 。当然,这个插件能干的事情不仅于此。

不知作为读者的你听到上述场景描述后,是否有种似曾相识的感觉?没错,lint-staged。

lint-staged,从git staged files变化中匹配你想要的文件,再执行你配置的commands。

Hawkeye,从git diff files变化中匹配你想要的文件,再执行你配置的commands。

需要注意的是,他们都依赖于husky改造git hooks的能力。

实现方案

1f63c45af522d3358c75fc3efe292c0d.png

例子

假设有一个已经安装了 hawkeye 和 husky 的项目, package.json 如下:

{  "name": "My project",  "version": "0.1.0",  "scripts": {  },  "husky": {    "hooks": {      "post-merge": "hawkeye"    }  },  "hawkeye": {    "package.json": ["npm install"]  }}
相关链接
  • semver 语义化版本 

    https://semver.org/lang/zh-CN/?spm=ata.13261165.0.0.552e2688ZKTpgz

  • semver(1) -- The semantic versioner for npm

    https://github.com/npm/node-semver?spm=ata.13261165.0.0.552e2688ZKTpgz

  • 2018 年了,你还是只会 npm install 吗?

    https://juejin.im/post/5ab3f77df265da2392364341?spm=ata.13261165.0.0.552e2688ZKTpgz

  • npm install algorithm

    https://docs.npmjs.com/cli/install?spm=ata.13261165.0.0.552e2688ZKTpgz#algorithm

  • npm dedupe

    https://docs.npmjs.com/cli/dedupe.html?spm=ata.13261165.0.0.552e2688ZKTpg

  • npm install的实现原理

    https://www.zhihu.com/question/66629910?spm=ata.13261165.0.0.552e2688ZKTpgz

  • [译] 理解 NPM 5 中的 lock 文件

    https://juejin.im/post/5943849aac502e006b84ce07?spm=ata.13261165.0.0.552e2688ZKTpgz

  • package-lock.json file not updated after package.json file is changed

    https://github.com/npm/npm/issues/16866?spm=ata.13261165.0.0.552e2688ZKTpgz

  • why is package-lock being ignored?

    https://github.com/npm/npm/issues/17979?spm=ata.13261165.0.0.552e2688ZKTpgz

  • lint-staged

    https://github.com/okonet/lint-staged?spm=ata.13261165.0.0.552e2688ZKTpgz

  • hawkeye

    https://github.com/stormqx/hawkeye?spm=ata.13261165.0.0.552e2688ZKTpgz

推荐阅读

我的公众号能带来什么价值?(文末有送书规则,一定要看)每个前端工程师都应该了解的图片知识(长文建议收藏)为什么现在面试总是面试造火箭?

41f1485158585b0348083295ca394f4b.png

7b39f00788b49b357a074cd34c977888.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385966.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

万字总结!腾讯、字节跳动面经已发

二、常见的并发问题 1、脏读 一个事务读取了另一个事务未提交的数据 2、不可重复读 一个事务对同一数据的读取结果前后不一致。两次读取中间被其他事务修改了 3、幻读 幻读是指事务读取某个范围的数据时&#xff0c;因为其他事务的操作导致前后两次读取的结果不一致。幻读…

ncbi查找目的基因序列_NCBI大搜索之目的基因寻踪

NCBI大搜索之目的基因寻踪最近经常碰到查找目的基因的问题&#xff0c;那今天就讲一下如何利用NCBI数据库查找目的基因&#xff01;NCBI(National Center For Biotechnology Information),美国国家生物技术信息中心&#xff0c;分子生物学&#xff0c;生物化学及遗传学领域常用…

万字长文!2020-2021京东Java面试真题解析

我整理的spring学习笔记&#xff1a; 像spring这种知识点我们不能盲目的学习&#xff0c;首先我们得有一套学习路线&#xff0c;我总结了一套spring的学习思维导图&#xff0c;今天通过我整理的Spring学习路线.xmind给大家分析spring需要掌握的一些核心知识点。 spring的特点&…

echarts label固定位置_ECharts+百度地图网络拓扑应用

前一篇谈及到了ECharts整合HT for Web的网络拓扑图应用&#xff0c;后来在ECharts的Demo中看到了有关空气质量的相关报表应用&#xff0c;就想将百度地图、ECharts和HT for Web三者结合起来也做一个类似空气质量报告的报表拓扑图应用&#xff0c;于是有了下面的Demo&#xff1a…

三年Java开发,你连基础的JVM运行时内存布局都忘了

面&#xff1a;为什么要使用双亲委派机制去加载类&#xff1f; 答&#xff1a;避免多份同样字节码的加载&#xff0c;浪费内存。 类的加载方式 隐式加载&#xff1a;new显示加载&#xff1a;loadClass、forName等 类的装载过程如下图&#xff1a; 面&#xff1a;loadClass和…

vue实现可编辑的文字_苹果还自带文字转语音,只要一键按下便可实现,今天分享给大家...

如果想将文字转成语音&#xff0c;那大家平时都是怎么操作&#xff1f;下面小编就为大家介绍手机&#xff0c;电脑上都可以使用的方法&#xff0c;让我们一起来看看吧&#xff01;一、手机端操作1、苹果手机其实苹果手机就自带了文字转语音功能&#xff0c;只要打开手机&#x…

三面美团Java岗,面试竟然被这31道Java基础题难倒了

01 分布式限流&#xff1a;NginxZooKeeper 1.1 分布式限流之Nginx 请解释一下什么是 Nginx? 请列举 x Nginx 的一些特性。 请列举 x Nginx 和 和 Apache 之间的不同点 请解释 x Nginx 如何处理 P HTTP 请求。 在 x Nginx 中&#xff0c;如何使用未定义的服务器名称来阻止…

海龟绘图小动物_震惊!被塑料绳勒成两半的海龟

海洋&#xff0c;其实离人类很近&#xff0c;我们在追逐沙滩和日落&#xff0c;享受美味的海鲜的时候&#xff0c;可曾想到我们平时的一些很随意的行为&#xff0c;会给一些海洋生物带来无法恢复的伤害&#xff0c;甚至夺取它们的生命。或许人们的冷漠无知尚未得到惩罚&#xf…

上海大厂Java面试经历:初步理解类加载运行机制和类加载过程

volatile相关经典面试题 谈谈volatile的特性volatile的内存语义说说并发编程的3大特性什么是内存可见性&#xff0c;什么是指令重排序&#xff1f;volatile是如何解决java并发中可见性的问题volatile如何防止指令重排volatile可以解决原子性嘛&#xff1f;为什么&#xff1f;v…

mysql数据库优化面试

前言 现在Java程序员面试都是因为没有丰富的工作经验和自己过硬的技术&#xff0c;所有都不知道一般互联网应该会问什么技术问题&#xff0c;加上自己可能去面试的时候没有准备的太充分&#xff0c;一面试刚跟面试官扯几个面试题就不知道自己在哪里了&#xff0c;被怼的体无完…

mysql数据库备份方式,跳槽大厂必看!

NO1&#xff1a;说说zookeeper是什么&#xff1f; ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff08;Chubby是不开源的&#xff09;&#xff0c;它是集群的管理者&#xff0c;监视着集群中各个节点…

mysql数据库安装教程32位,看这一篇就够了!

字节跳动 ⾯试前⼀⾯⼆⾯⼩结 ⾯试前 头条的⾯试是三家⾥最专业的&#xff0c;每次⾯试前有专⻔的HR和你约时间&#xff0c;确定OK后再进⾏⾯试。每次都是通过视频⾯试&#xff0c;因为都是之前都是电话⾯或现场⾯&#xff0c;所以视频⾯试还是有点不⾃然。也有⼈觉得视频⾯试…

java cron工具类_Java工具类之:包装类

我们都知道&#xff0c;JDK 其实给我们提供了很多很多 Java 开发者已经写好的现成的类&#xff0c;他们其实都可以理解成工具类&#xff0c;比如我们常见的集合类&#xff0c;日期相关的类&#xff0c;数学相关的类等等&#xff0c;有了这些工具类&#xff0c;你会发现它能很大…

mysql数据库安装,真香!

Spring Security观后感——手绘思维脑(供参考) 手绘的思维导图&#xff0c;是我自己根据自身的情况读完这套阿里出品的Spring Security王者晋级文档之后所绘的&#xff0c;相当于是一个知识的总结与梳理&#xff0c;我将其分为“核心组件”与“工作原理/认证流程”。 Spring Se…

mysql数据库实用教程答案

前言 数据库相关的面试题早已成为了一线互联网大厂面试的家常菜&#xff0c;如果你对数据库不太熟悉&#xff0c;我劝你不要轻易面试大厂。那么&#xff0c;为什么数据库成了大厂面试的家常菜呢&#xff1f;主要原因当然还是海量数据。 无论对于刚入行的小白还是有几年Java开…

在idea中新建的text文件_开发属于自己的第一款 IDEA 插件!

往期热门文章&#xff1a;1、《往期精选优秀博文都在这里了&#xff01;》2、又一个程序员跑路删库跑路被抓了&#xff0c;导致服务器瘫痪 36 个小时!3、恕我直言&#xff0c;有了这款 IDEA 插件&#xff0c;你可能只需要写 30% 的代码。。。4、Java8 的 Stream API 的确牛X&am…

mysql数据库开发的36条军规

前言 一般的小项目&#xff0c;比如几百人左右访问的项目&#xff0c;访问量几万的项目&#xff0c;如果想用缓存&#xff0c;单机实例完全够用。小黄图就是用的阿里云256MB配置的Redis缓存&#xff0c;日几千的访问量是妥妥够用的了。Redis号称可以支撑10wqps&#xff0c;当然…

mysql数据库引擎怎么看,值得收藏!

什么是Service Mesh 作为Service Mesh技术探索和实践的先行者&#xff0c;全球第一个真正的Service Mesh项目Linkerd负责人、Buoyant公司创始人兼CEO William Morgan第一次完整地阐述了Service Mesh。按照William Morgan的定义&#xff0c;Service Mesh是一个致力于解决服务间…

mysql数据库文件位置

01 前言 辛苦奋斗两个月&#xff0c;秋招终于圆满收官&#xff0c;拿到了头条、字节、菜鸟、腾讯、网易的offer&#xff0c;这要多亏了意外得到的这份资料文档&#xff0c;这么多面试全都靠它了&#xff0c;哈哈~~有好东西还是要分享出来给大家&#xff0c;一起学习呀 Java核心…

mysql数据库连接失败,挑战大厂重燃激情!

一、阿里 &#xff08;会员中台&#xff09; 1. Java 重写hashcode()是否需要重写equals()&#xff0c;不重写会有什么后果 2. 并发 自旋锁和阻塞锁的区别公平锁和非公平锁的区别jdk中哪种数据结构或工具可以实现当多个线程到达某个状态时执行一段代码栅栏和闭锁的区别如何…