当前位置: 首页 > 后端技术 > Node.js

深挖NPM机制

时间:2023-04-03 17:34:31 Node.js

使用NPM安装时,经常会出现包冲突(比如多个主模块的子模块版本不一致等),导致安装过程中或大或小的各种问题开发过程。这里将介绍以下所有内容:NPM主要安装方式NPM包信息查询NPM安装机制(主要)安装&查询命令NPM各种安装方式npminstallpackageName[@next|@versionNumber]当node_modules中没有指定模块时安装,(不要检查~/.npm目录)npminstallpackageName--f|--force一个模块无论是否已经安装都会被重新安装。npmupdatepackageName如果远程版本较新或者本地版本不存在,安装npmquery服务npm通过registry的查询服务知道各个模块的最新版本。可以通过npmviewpackageName[version]npm安装机制查询对应模块的信息输入npminstall命令回车后,会经历以下几个阶段(以npm5.5.1为例):1.执行项目本身的preinstall如果当前npm项目定义了preinstallhook,此时会执行。2.确定一级依赖模块首先需要确定项目中的一级依赖,即dependencies和devDependencies属性中直接指定的模块(假设npminstall处没有添加参数)这次)。项目本身就是整个依赖树的根节点。每个一级依赖模块是根节点下的子树。npm会启动多个进程,逐步从每个一级依赖模块中寻找更深层次的节点。如果指定的模块在查询node_modules目录下已经存在,则不会重新安装。3.获取模块获取模块是一个递归的过程,分为以下几个步骤:获取模块信息在下载模块之前,首先要判断模块的版本。这是因为package.json往往是语义版本(semver,semanticversion)。此时如果版本描述文件(npm-shrinkwrap.json或package-lock.json)中有模块信息,则可以直接获取。如果没有,可以从仓库Get(查询registry)获取。比如packaeg.json中某个包的版本是^1.1.0,npm会去仓库获取最新版本,形式为1.x.x。获取模块实体。在上一步中,会得到模块的压缩包地址(resolved字段),npm会通过这个地址去查看本地缓存,如果缓存中有,则直接取,如果没有,它将从仓库下载。查找模块依赖项。如果存在依赖关系,则返回步骤1。如果不存在,则停止。4.模块扁平化(dedupe)一步得到完整的依赖树,其中可能包含大量重复的模块。比如模块A依赖loadsh,模块B也依赖lodash。npm3之前会严格按照依赖树的结构安装,所以会造成模块冗余。从npm3版本开始,默认增加了一个dedupe进程。它遍历所有节点,将模块逐个放置在根节点下,这是第一层节点模块。当发现重复模块时,它们将被丢弃。这里需要一个重复模块的定义,指的是同名模块和semver兼容性。每个semver对应一个版本允许范围。如果两个模块的版本允许范围重叠,则可以获得兼容的版本而不必具有相同的版本号。这允许在重复数据删除过程中删除更多冗余模块。.比如node-modules下的foo模块依赖lodash@^1.0.0,bar模块依赖lodash@^1.1.0,所以^1.1.0为兼容版本。而当foo依赖lodash@^2.0.0,bar依赖lodash@^1.1.0时,根据semver的规则,两者没有兼容的版本。一个版本将放在node_modules中,另一个将保留在依赖树中。例如,假设一个依赖树原本是这样的:node_modules--foo----lodash@version1--bar----lodash@version2假设version1和version2是兼容版本,去重后会变成如下形式:node_modules--foo--bar--lodash(retainedversionsarecompatibleversions)假设version1和version2是不兼容的版本,之后的版本在依赖树中保留:node_modules--foo--lodash@version1--bar----lodash@version25。安装模块此步骤将更新项目中的node_modules并执行模块中的生命周期函数(按照预安装、安装、安装后的顺序)。6.执行项目本身的生命周期。如果当前npm项目定义了hooks,此时会执行(按照install、postinstall、prepublish、prepare的顺序)。最后一步是生成或更新发布描述文件,npminstall过程完成。文末分享同步在:https://github.com/zhongmeizh...参考阮一峰个人博客npm实现原理