共计 4177 个字符,预计需要花费 11 分钟才能阅读完成。
npm
前端工程化离不开 npm(node package manager)或者 Yarn 这些管理工具。npm 或 Yarn 在工程项目中,除了负责依赖的装置和保护以外,还能通过 npm scripts 串联起各个职能局部,让独立的环节主动运行起来。
npm 诞生背景
npm 由程序员 isaacs(https://github.com/isaacs)创造,他的初步思路是集中管理所有的模块,所有的模块都上传到仓库中(registry)。在模块内创立 package.json 标注模块的根本信息,像模块名、版本、依赖库等等,而后通过 npm publish 公布模块上传倒 npm 仓库中(registry),最初通过 npm install 装置模块,模块装置到 node_modules 目录下。npm 于 2014 年商业化,2020 年被 Github 收买。
npm 介绍
npm 解决的外围问题是模块治理问题,npm 蕴含 cli 脚手架、模块仓库、官网 (https://www.npmjs.com/) 三大部分。
对于 npm 的相干信息能够查阅:https://docs.npmjs.com/about-npm
产生一个模块首先通过 npm init 来创立一个模块,创立的模块中蕴含 package.json,咱们通过批改 name、version、dependencies 来确定模块的根本信息,如果模块想应用其余的模块则须要应用 npm install 来装置其余的模块,将模块下载到 node_modules 目录中,其中模块也蕴含 package.json 和 node_modules 这就是 npm 的标准。
当 module1 开发实现后,调用 npm publish 上传到 npm registry 中,registry 蕴含两局部,一部分是 public 公开的,任何人都能应用,另一部分是 private 公有的,当初 npm 商业化后次要靠公有仓库免费。
public 共有仓库又分为两种,一种是一般的仓库,一种是组织的仓库。
常见的 npm 命令如下:
- npm init:创立模块
- npm install:装置模块
- npm publish:公布模块
- npm link:关联本地模块进行本地开发
- npm config:查看或调整本地配置
- npm run:调用 scripts
npm 的局限
npm 只能解决模块的高校治理和获取问题,无奈解决性能加载性能问题。所以模块化创造后,制约其广泛应用的因素就是性能因素。
npm 外部机制和外围原理
咱们先来看看 npm 的外围指标:
Bring the best of open source to you, your team and your company.
给你和你的团队、你的公司带来最好的开源库和依赖。
通过这句话,咱们能够晓得 npm 最重要的一环是装置和保护依赖。在平时开发中,“删除 node_modules,从新 npm install”是一个百试不爽的解决 npm 装置类问题的办法。
npm 的装置机制和背地思维
它会优先装置依赖包到以后我的项目目录,使得不同利用我的项目的依赖各成体系,同时还加重了包作者的 API 兼容性压力,但这样做的缺点也很显著:如果咱们的我的项目 A 和我的项目 B,都依赖了雷同的公共库 C,那么公共库 C 个别都会在我的项目 A 和我的项目 B 中,各被装置一次。这就阐明,同一个依赖包可能在咱们的电脑上进行屡次装置。
当然,对于一些工具模块比方 supervisor 和 gulp,你依然能够应用全局装置模式,这样不便注册 path 环境变量,咱们能够在任何中央间接应用 supervisor、gulp 这些命令。(不过,个别还是倡议不同我的项目保护本人部分的 gulp 开发工具以适配不同我的项目需要。)
npm 的装置机制如下图所示:
npm install 执行之后,首先,查看并获取 npm 配置,这里的优先级为:我的项目级的 .npmrc 文件 > 用户级的 .npmrc 文件 > 全局级的 .npmrc 文件 > npm 内置的 .npmrc 文件。
而后查看我的项目中是否有 package-lock.json 文件。
如果有,则查看 package-lock.json 和 package.json 中申明的依赖是否统一:
- 统一,间接应用 package-lock.json 中的信息,从缓存或网络资源中加载依赖;
- 不统一,依照 npm 版本进行解决(不同 npm 版本解决会有不同,具体解决形式如图所示)。
如果没有,则依据 package.json 递归构建依赖树。而后依照构建好的依赖树下载残缺的依赖资源,在下载时就会查看是否存在相干资源缓存:
- 存在,则将缓存内容解压到 node_modules 中;
- 否则就先从 npm 近程仓库下载包,校验包的完整性,并增加到缓存,同时解压到 node_modules。
最初生成 package-lock.json。
构建依赖树时,以后依赖我的项目不论其是间接依赖还是子依赖的依赖,都应该依照扁平化准则,优先将其搁置在 node_modules 根目录(最新版本 npm 标准)。在这个过程中,遇到雷同模块就判断已搁置在依赖树中的模块版本是否合乎新模块的版本范畴,如果合乎则跳过;不合乎则在以后模块的 node_modules 下搁置该模块(最新版本 npm 标准)。
图中表明的 npm 不同版本的不同解决状况,并学会从这种“历史问题”中总结 npm 应用的团队最佳实际:同一个我的项目团队,应该保障 npm 版本的统一。
npm 缓存机制
对于一个依赖包的同一版本进行本地化缓存,是当代依赖包管理工具的一个常见设计。应用时要先执行以下命令:
npm config get cache
通过这行命令能够失去配置缓存的根目录在 C:\Users\ 用户名 \AppData\Local\npm-cache(mac 在 /Users/ 用户名 /.npm),咱们 cd 进入 C:\Users\ 用户名 \AppData\Local\npm-cache 中能够发现_cacache 文件。在 npm v5 版本之后,缓存数据均放在根目录中的_cacache 文件夹中。
能够应用以下命令革除 C:\Users\ 用户名 \AppData\Local\npm-cache\_cacache 中的文件
npm cache clean --force
在_cacache 目录下,有三个目录:
- content-v2
- index-v5
- tmp
其中 content-v2 外面根本都是一些二进制文件。为了使这些二进制文件可读,咱们把二进制文件的扩展名改为 .tgz,而后进行解压,失去的后果其实就是咱们的 npm 包资源。
而 index-v5 文件中,咱们采纳跟刚刚一样的操作就能够取得一些描述性的文件,事实上这些内容就是 content-v2 里文件的索引。
那么这些缓存是如何生成的呢?
当 npm install 执行时,通过 pacote 把相应的包解压在对应的 node_modules 上面。npm 在下载依赖时,先下载到缓存当中,再解压到我的项目 node_modules 下。pacote 依赖 npm-registry-fetch 来下载包,npm-registry-fetch 能够通过设置 cache 属性,在给定的门路下依据 IETF RFC 7234 生成缓存数据。
而后在每次装置资源时,依据 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}。
npm 的应用技巧
配置 npm init 默认字段来自定义 npm init 的内容
npm config set init.author.name "test"
npm config set init.author.email "test@gmail.com"
npm config set init.author.url "test.com"
npm config set init.license "MIT"
更多信息见:npm-config
利用 npm link,高效率在本地调试以验证包的可用性
应用 npm link 能够将模块链接到对应的业务我的项目中运行,从工作原理上总结,npm link 的实质就是软链接,它次要做了两件事:
- 为指标 npm 模块创立软链接,将其链接到全局 node 模块装置门路 C:\Users\ 用户名 \AppData\Roaming\npm\node_modules(mac 在 /usr/local/lib/node_modules/)中
- 为指标 npm 模块的可执行 bin 文件创建软链接,将其链接到全局 node 命令装置门路 /usr/local/bin/ 中。
npx 的作用
npx 由 npm v5.2 版本引入,解决了 npm 的一些应用疾速开发、调试,以及我的项目内应用全局模块的痛点。
在传统 npm 模式下,如果咱们须要应用代码检测工具 ESLint,就要先通过 npm install 装置
npm install eslint --save-dev
而后在我的项目根目录下执行:
./node_modules/.bin/eslint --init
./node_modules/.bin/eslint yourfile.js
而应用 npx 就简略多了,你只须要上面 2 个操作步骤:
npx eslint --init
npx eslint yourfile.js
这是因为它能够间接执行 node_modules/.bin 文件夹下的文件。在运行命令时,npx 能够主动去 node_modules/.bin 门路和环境变量 $PATH 外面查看命令是否存在,而不须要再在 package.json 中定义相干的 script。
npx 另一个更实用的益处是:npx 执行模块时会优先装置依赖,然而在装置执行后便删除此依赖,这就防止了全局装置模块带来的问题。
例如咱们应用 create-react-app 创立工程。
npx create-react-app cra-project
npx 会将 create-react-app 下载到一个长期目录,应用当前再删除
相干文章
- 前端工程化 – 分析 npm 的包管理机制