问题思考:
1. package-lock.json的意义?
2. npm 拉包机制和源码实现 ?
3. yarn?
4. package-lock.json有没有必要提交?
5. npmrc?
6. npx?
7. 还有 import 直接导入包的cnd地址 和 安装到本地有啥区别:(打包时候的vender 大小 和 是否在缓存获取的取舍)?
...
1. package-lock.json的意义 蹦到官网
This file is intended to be committed into source repositories, and serves various purposes:
1. Describe a single representation of a dependency tree such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies.
2. Provide a facility for users to "time-travel" to previous states of node_modules without having to commit the directory itself.
3. Facilitate greater visibility of tree changes through readable source control diffs.
4. Optimize the installation process by allowing npm to skip repeated metadata resolutions for previously-installed packages.
5. As of npm v7, lockfiles include enough information to gain a complete picture of the package tree, reducing the need to read package.json files, and allowing for significant performance improvements.
该文件旨在提交到源存储库中,并具有多种用途:
1. 描述一个依赖关系树的单一表示,这样可以确保队友,部署和持续集成安装完全相同的依赖关系。
2. 为用户提供一种工具,使其可以“时间旅行”到以前的状态, node_modules而不必提交目录本身。
3. 通过可读的源代码控制差异,提高树更改的可见性。
4. 通过允许npm跳过先前安装的程序包的重复元数据解析来优化安装过程。
5. 从npm v7开始,锁定文件包含足够的信息以获取软件包树的完整图片,从而减少了读取package.json 文件的需求并显着提高了性能。
2. npm 拉包机制和源码实现 来自拉勾教育,收费的那种0.0
2.1 npm install 之后执行了哪些步骤?
1. 检查并获取 npm 配置,这里的优先级为:项目级的 .npmrc 文件 > 用户级的 .npmrc 文件> 全局级的 .npmrc 文件 > npm 内置的 .npmrc 文件。
2. 检查项目中是否有 package-lock.json 文件。
3. 3.1 如果有,则检查 package-lock.json 和 package.json 中声明的依赖是否一致:
a: 一致,直接使用 package-lock.json 中的信息,从缓存或网络资源中加载依赖;
b: 不一致,按照 npm 版本进行处理(不同 npm 版本处理会有不同,具体处理方式如图所示)。
3.2 如果没有,则根据 package.json 递归构建依赖树。然后按照构建好的依赖树下载完整的依赖资源,在下载时就会检查是否存在相关资源缓存:
a: 存在,则将缓存内容解压到 node_modules 中;
b: 否则就先从 npm 远程仓库下载包,校验包的完整性,并添加到缓存,同时解压到 node_modules。
4. 生成 package-lock.json。
2.2 npm 缓存机制
1. 获取配置缓存的根目录:npm config get cache
2. 缓存目录的内容介绍: 打开_cacache文件后,可以看到三个目录content-v2, index-v5, tmp (空文件)。
2.1 content-v2 里面基本都是一些二进制文件。为了使这些二进制文件可读,我们把二进制文件的扩展名改为 .tgz,然后进行解压,得到的结果其实就是我们的 npm 包资源。
2.2 index-v5 文件中,这些内容是 content-v2 里文件的索引。
3. 缓存如何被利用:
3.1 当 npm install 执行时,通过pacote把相应的包解压在对应的 node_modules 下面。npm 在下载依赖时,先下载到缓存当中,再解压到项目 node_modules 下。pacote 依赖npm-registry-fetch来下载包,npm-registry-fetch 可以通过设置 cache 属性,在给定的路径下根据IETF RFC 7234生成缓存数据。
3.2 在每次安装资源时,根据 package-lock.json 中存储的 integrity、version、name 信息生成一个唯一的 key,这个 key 能够对应到 index-v5 目录下的缓存记录。如果发现有缓存资源,就会找到 tar 包的 hash,根据 hash 再去找缓存的 tar 包,并再次通过pacote把对应的二进制文件解压到相应的项目 node_modules 下面,省去了网络下载资源的开销。注意,这里提到的缓存策略是从 npm v5 版本开始的。在 npm v5 版本之前,每个缓存的模块在 ~/.npm 文件夹中以模块名的形式直接存储,储存结构是:{cache}/{name}/{version}。
3. yarn 来自拉勾教育,收费的那种0.0
3.1 yarn的介绍
当 npm 还处在 v3 时期时,一个叫作 Yarn 的包管理方案横空出世。2016 年,npm 还没有 package-lock.json 文件,安装速度很慢,稳定性也较差,而 Yarn 的理念很好地解决了以下问题。
1. 确定性:通过 yarn.lock 等机制,保证了确定性。即不管安装顺序如何,相同的依赖关系在任何机器和环境下,都可以以相同的方式被安装。(在 npm v5 之前,没有 package-lock.json 机制,只有默认并不会使用的npm-shrinkwrap.json。)
2. 采用模块扁平安装模式:将依赖包的不同版本,按照一定策略,归结为单个版本,以避免创建多个副本造成冗余(npm 目前也有相同的优化)。
3. 网络性能更好:Yarn 采用了请求排队的理念,类似并发连接池,能够更好地利用网络资源;同时引入了更好的安装失败时的重试机制。
4. 采用缓存机制,实现了离线模式(npm 目前也有类似实现)。
3.2 yarn安装机制和背后思想
Yarn 的安装过程主要有以下 5 大步骤:检测(checking)→ 解析包(Resolving Packages) → 获取包(Fetching Packages)→ 链接包(Linking Packages)→ 构建包(Building Packages)
1. 检测包(checking): 这一步主要是检测项目中是否存在一些 npm 相关文件,比如 package-lock.json 等。如果有,会提示用户注意:这些文件的存在可能会导致冲突。在这一步骤中,也会检查系统 OS、CPU 等信息。
2. 解析包(Resolving Packages):这一步会解析依赖树中每一个包的版本信息。
2.1 首先获取当前项目中 package.json 定义的 dependencies、devDependencies、optionalDependencies 的内容,这属于首层依赖。
2.2 接着采用遍历首层依赖的方式获取依赖包的版本信息,以及递归查找每个依赖下嵌套依赖的版本信息,并将解析过和正在解析的包用一个 Set 数据结构来存储,这样就能保证同一个版本范围内的包不会被重复解析。
2.2.1 对于没有解析过的包 A,首次尝试从 yarn.lock 中获取到版本信息,并标记为已解析;
2.2.2 如果在 yarn.lock 中没有找到包 A,则向 Registry 发起请求获取满足版本范围的已知最高版本的包信息,获取后将当前包标记为已解析。
总之,在经过解析包这一步之后,我们就确定了所有依赖的具体版本信息以及下载地址。
3. 获取包(Fetching Packages):首先需要检查缓存中是否存在当前的依赖包,同时将缓存中不存在的依赖包下载到缓存目录。
如何判断缓存中是否存在当前的依赖包?
Yarn 会根据 cacheFolder+slug+node_modules+pkg.name 生成一个 path,判断系统中是否存在该 path,如果存在证明已经有缓存,不用重新下载。这个 path 也就是依赖包缓存的具体路径。
对于没有命中缓存的包,Yarn 会维护一个 fetch 队列,按照规则进行网络请求。如果下载包地址是一个 file 协议,或者是相对路径,就说明其指向一个本地目录,此时调用 Fetch From Local 从离线缓存中获取包;否则调用 Fetch From External 获取包。最终获取结果使用 fs.createWriteStream 写入到缓存目录下。
4. 链接包(Linking Packages):这一步是将项目中的依赖复制到项目 node_modules 下,同时遵循扁平化原则。在复制依赖前,Yarn 会先解析 peerDependencies,如果找不到符合 peerDependencies 的包,则进行 warning 提示,并最终拷贝依赖到项目中。5.
5. 构建包(Building Packages):如果依赖包中存在二进制包需要进行编译,会在这一步进行。
3.3 扁平化安装模式
举个🌰: 项目中有A依赖v1.0, A又依赖Bv1.0。
当项目新添加了 C 依赖,而它依赖另一个版本的 B v2.0。这时候版本要求不一致导致冲突,B v2.0 没办法放在项目平铺目录下的 node_moduls 文件当中,npm v3 会把 C 依赖的 B v2.0 安装在 C 的 node_modules 下:
接下来,在 npm v3 中,假如我们的 App 现在还需要依赖一个 D,而 D 也依赖 B v2.0 ,我们会得到如下结构:
为什么 B v1.0 出现在项目顶层 node_modules,而不是 B v2.0 出现在 node_modules 顶层呢?
假设这时候项目又添加了一个依赖 E ,E 依赖了 B v1.0 ,安装 E 之后,我们会得到这样一个结构:
如果我们想更新模块 A 为 v2.0,而模块 A v2.0 依赖了 B v2.0,npm v3 会怎么处理呢?它的结构如下:
为什么Bv1.0依然存在?
这时模块 B v2.0 分别出现在了 A、C、D 模块下——重复存在了。
通过这一系列操作我们可以看到:npm 包的安装顺序对于依赖树的影响很大。模块安装顺序可能影响 node_modules 内的文件数量。
这里一个更理想的依赖结构理应是:
过了一段时间,模块 E v2.0 发布了,并且 E v2.0 也依赖了模块 B v2.0 ,npm v3 更新 E 时会怎么做呢?
此时顶层已经有了B v2.0,如何删除嵌套层多余的B v2.0?
1. 删除node_modules, 重新安装。
2. npm dedupe命令
结构如下:
实际上,Yarn 在安装依赖时会自动执行 dedupe 命令。整个优化的安装过程,就是扁平化安装模式。
4. package-lock.json有没有必要提交?
看项目定位。
1. 如果开发一个应用,我建议把 package-lock.json 文件提交到代码版本仓库。这样可以保证项目组成员、运维部署成员或者 CI 系统,在执行 npm install 后,能得到完全一致的依赖安装内容。
2. 如果你的目标是开发一个给外部使用的库,那就要谨慎考虑了,因为库项目一般是被其他项目依赖的,在不使用 package-lock.json 的情况下,就可以复用主项目已经加载过的包,减少依赖重复和体积。
3. 如果我们开发的库依赖了一个精确版本号的模块,那么提交 lockfiles 到仓库可能会造成同一个依赖不同版本都被下载的情况。如果作为库开发者,真的有使用某个特定版本依赖的需要,一个更好的方式是定义 peerDependencies。因此,一个推荐的做法是:把 package-lock.json 一起提交到代码库中,不需要 ignore。但是执行 npm publish 命令,发布一个库的时候,它应该被忽略而不是直接发布出去。
5. npmrc 蹦官网
npm 的配置文件
npm gets its config settings from the command line, environment variables, and npmrc files.
The four relevant files are:
1. per-project config file (/path/to/my/project/.npmrc)
2. per-user config file (~/.npmrc)
3. global config file ($PREFIX/etc/npmrc)
4. npm builtin config file (/path/to/npm/npmrc)
6. npx
npm 从5.2版开始,增加了 npx 命令。Node 自带 npm 模块,所以可以直接使用 npx 命令。万一不能用,就要手动安装一下。
npm install -g npx使用场景:
1. 调用项目安装的模块, (npx mocha --version)
2. 避免全局安装模块 (npx http-server)
3. 使用不同版本的 node (npx node@0.12.8 -v)
4. 执行 GitHub 源码 (npx github:piuccio/cowsay hello)
注意,远程代码必须是一个模块,即必须包含package.json和入口脚本。
7. cdn vs local?
影响因素:
1. 离线开发?
2. cdn大文件,local小文件?
3. 预缓存保证?
4. 禁止访问(国家/地区 封锁某些cdn服务的域或IP)
5. 两点故障?(站点宕机)
6. 安全性?(修改代码,收集用户系统数据。。。)
7. 失去控制权?(网站文件控制权)
8. 访问速度?
参考: