关于前端:前端依赖管理那点事儿

4次阅读

共计 5360 个字符,预计需要花费 14 分钟才能阅读完成。

次要内容

  • 📝 什么是依赖
  • 📤 依赖从哪来
  • 🤹 装置到哪儿
  • 🎥 版本控制
  • 🧑‍💻 哪些须要装,哪些不须要
  • 🎨 package-lock.json
  • 🛠 npm install 过程回顾

什么是依赖

有时候,依赖是一堆 可执行的代码
有时候,依赖只是 一句申明

1. 当咱们的业务逻辑中应用到 Vue 时,咱们只须要依赖(引入)它,咱们就能够应用它的能力

import vue from 'vue'

or

<script src="https://unpkg.com/vue@next"></script>

此时,依赖就是获取一堆可用的代码

2. 当咱们在开发组件库时,咱们须要应用到 Vue 中的一些办法

import {ref} from 'vue'

在这种场景下,组件所应用的 vue.js,实际上是宿主环境所依赖的 Vue

此时,依赖仅仅是一个申明

依赖从哪来

获取依赖的起源有哪些

  • 动态文件打包
  • CDN 引入
  • 仓库级援用
  • 从 npm 源装置

动态文件打包

前端资源通过相对路径进行引入,整体打包到我的项目中。

CDN 引入

长处:

  • 多域复用
  • 就近传输
  • 通过跨域达到 冲破浏览器并发限度 的成果
  • ……

毛病:

引入公共 CDN 资源,须要评估危险,防止 CDN 域名净化后,资源异样

仓库级援用

git submodule

文章参考:《Git 中 submodule 的应用》

从 npm 源装置

1. 公网 npm registry

npm install vue

2. 公有 npm registry

npm install @xxfe/babel-plugin-icover --registry=http://ires.xxcorp.com/repository/xxnpm/

.npmrc 指定仓库源地址

registry=http://ires.xxcorp.com/repository/xxnpm/
or
// 特定命名空间下
@xxfe:registry=http://ires.xxcorp.com/repository/xxnpm/

3. 指定 git 仓库

{
  "name": "foo",
  "version": "0.0.0",
  "dependencies": {"express": "git+ssh://git@github.com:npm/cli.git#v1.0.27"}
}

通过指定 协定 仓库地址 以及 tag/commit-hash 等信息,能够精准指定到某个版本的代码

文档参考:《npm docs》

4.post-install 玩法

从命名上可能看出,post-install 的意思是指 install 之后之后会被动触发的一个钩子。
通过在这个钩子内执行脚本,你能够去下载任何你想要的内容,包含但不限于:.exe.avi.pdf 等等 …

npm install 执行的过程会经验三个勾子办法:

  • preinstall
  • install
  • postinstall

npm install 命令发动后,依据工程定义决定是否执行 preinstall
installpostinstallnpm install 命令必然会执行的阶段

文档参考:

  • 《npm docs》
  • 《滥用 npm 库导致数据暗渡》

装置到哪儿

node_modules?当然是对的!然而并不精确。

依赖天堂

说到 node_modules,总是离不开要看看它的 依赖天堂图

咱们别离以 ReactVue 为例,独自装置和通过 cli 工具进行装置,node_modules的装置后果:

  • 独自装置 ReactReactDOM,占用 5.2M 空间;
  • 独自装置 Vue(3.x),占用 16.3M 空间;
  • 应用 create-react-app 创立一个空白 React 我的项目,占用 344.8M 空间;
  • 应用 vue-cli 创立一个空白 Vue 我的项目,占用 163.9M 空间。

node_modules 层级

npm2.x 版本 node_modules 层级 – 递归式

先定义一种语法 A{B,C} 代表 A 包依赖了 B 包和 C

A{D@1.0.0}, B{D@2.0.0}, C{D@1.0.0}

├── node_modules
    │   ├── A@1.0.0
    │   │   └── node_modules
    │   │   │   └── D@1.0.0
    │   ├── B@1.0.0
    │   │   └── node_modules
    │   │   │   └── D@2.0.0
    │   └── C@1.0.0
    │   │   └── node_modules
    │   │   │   └── D@1.0.0

能够设想,这样做确实能尽量保障每个模块本身的可用性。然而,当我的项目规模达到肯定水平时,也会造成许多问题:

  • 依赖树的层级十分深。如果须要定位某依赖的依赖,很难找到该依赖的文件所在(例如,如果想定位模块 D,就不得不先晓得他在依赖树中的地位);
  • 不同的依赖树分支里,可能有大量实际上是同样版本的依赖(例如,D@1.0.0AC 下版本是统一的);
  • 装置时额定下载或拷贝了大量反复的资源,并且实际上也占用了大量的硬盘空间资源等(例如,D 模块在依赖目录中呈现了三次);
  • 装置速度慢,甚至因为目录层级太深导致文件门路太长的缘故,在 windows 零碎下删除 node_modules 文件夹也可能失败!

这堪称:子又生孙,孙又生子,子子孙孙无穷尽也 ……

npm3.x 版本 node_modules 层级 – 扁平式

npm3.x 版本后,npm 采纳了更正当的形式去解决依赖天堂问题。npm3.x 尝试把依赖以及依赖的依赖都尽量的平铺在我的项目根目录下的 node_modules 文件夹下以 共享应用;如果遇到因为须要的版本要求不统一导致抵触,没方法放在平铺目录下的,回退到 npm2.x 的解决形式,在该模块下的 node_modules 里寄存抵触的模块。

A{D@1.0.0}, B{D@1.0.0}, C{D@2.0.0}

├── node_modules
    │   ├── A@1.0.0
    │   ├── B@1.0.0
    │   │── C@1.0.0
    │   │   └── node_modules
    │   │   │   └── D@2.0.0
    │   ├── D@1.0.0

幽灵依赖问题

A{D@2.0.0},B

├── node_modules
    │   ├── A@1.0.0
    │   ├── B@1.0.0
    │   ├── D@2.0.0
const A = require('A');
const D = require('D'); ???

<li> 依赖兼容问题 – 版本不兼容 </li>
<li> 依赖缺失问题 – 缺失报错 </li>

不确定性问题

A@1.0.0{C@1.0.0},B@1.0.0{C@2.0.0}

node_modules
├── A@1.0.0
├── B@1.0.0
│ └── node_modules
│   └── C@2.0.0
├── C@1.0.0

node_modules
├── A@1.0.0
│ └── node_modules
│   └── C@1.0.0
├── B@1.0.0
├── C@2.0.0
依赖分身 / 多重依赖问题

A{B@1.0.0}, C{B@2.0.0}, D{B@1.0.0}, E{B@2.0.0}

node_modules
├── A@1.0.0
├── B@1.0.0
├── D@1.0.0
├── C@1.0.0
│ └── node_modules
│   └── B@2.0.0
└── E@1.0.0
  └── node_modules
    └── B@2.0.0

版本控制

  • ^1.1.0 和 ~1.1.0 的区别是什么?
  • 1.01.02 是否非法?
  • 1.0.1、1.0.1-alpha.2、1.0.1-rc.2 这三个版本号由大到小的程序是什么?
  • vue@latest 应该命中哪个版本?由谁决定?那么 vue@v2-beta 呢?

在软件治理畛域里存在着被称作“依赖天堂”的死亡之谷。

零碎规模越大,退出的包越多,你就越有可能在将来的某一天发现自己曾经深陷失望之中。

在依赖高的零碎中公布新版本包可能很快会成为噩梦。如果依赖关系过高,可能面临版本控制被锁死的危险(必须对每一个依赖包改版能力实现某次降级)。如果依赖关系过于涣散,又将无奈防止版本的凌乱(假如兼容于将来的多个版本已超出了正当的数量)。

当你我的项目的停顿因为版本依赖被锁死或版本凌乱变得不够简便和牢靠,就意味着你正处于依赖天堂之中。

这就是为什么须要应用语义化版本的起因

SemVer

语义化的版本控制(Semantic Versioning),简称语义化版本,英文缩写为 SemVer。

语义化版本通过一组简略的规定及条件来束缚版本号的配置和增长。这些规定是依据(但不局限于)曾经被各种关闭、开发源码软件所宽泛应用的常规所设计。

格局

语义化版本格局:主版本号. 次版本号. 订正号(MAJOR.MINOR.PATCH)。

版本号递增规定如下:

  • 主版本号(MAJOR):通常只有在重构、API 不向下兼容时才会进行降级;
  • 次版本号(MINOR):通常在减少向下兼容新个性时降级此版本号;
  • 订正号(PATCH):通常在公布向下兼容的问题修复时更新

后行版本号及版本编译信息能够加到 “主版本号. 次版本号. 订正号” 的前面,作为延长。

后行版本号

Snapshot:快照,也被称为开发版,处于开发阶段。这个版本的代码禁止用于生产环境。

Alpha (α):内测版,外部交换或业余测试人员测试应用。

Preview:预览版,与 Alpha 相似,有时还会细分 M1,M2 版本。

Beta (β):公测版,业余爱好者大规模测试应用,存在一些 Bug,不适宜个别用户应用。

Gamma (λ):比拟成熟的测试版。

RC (Release Candidate):候选版本,处于 Gamma 阶段,该版本曾经实现了全副性能并革除了大量的 Bug。
到了这个阶段,只会修复 Bug,不会对软件做任何大的更改。

一般来说,Alpha -> Beta -> Gamma 是迭代的关系,RC1 -> RC2 是取舍的关系。

Release:发行版本,正式发行的版本,曾经通过测试,个别不会呈现重大的 Bug,适宜个别用户应用。
对于不开源的软件,Release 可能是带有收费应用工夫限度的版本。

Stable:稳定版,同 Release 版本。

版本匹配策略

咱们会发现装置的依赖版本会呈现:^1.1.0 或 ~1.1.0,这是什么意思呢?

含糊匹配策略
  • ^1.0.1、1、1.x 代表了能够命中主版本统一、但更新的版本号。
  • ~1.0.1、1.1、1.1.x 代表了能够命中主版本、次版本统一、但更新的版本号。
  • * 和 x 能够命中所有新公布的版本号。
dist-tag 和版本号
npm install vue@latest
# 或者换一句
npm install vue@next

npm 指令:dist-tag

npm dist-tag add <pkg>@<version> [<tag>]

// 公布

npm publish --tag beta

beta、latest、next、preview、legacy、v2-beta ……


哪些须要装,哪些不须要

Q: 你能一口气说分明我的项目里 node_modules 里的那些依赖都是怎么来的吗?为什么下载了它们,以及为什么只下载了它们?

package.json 中的各种 dependencies

  • dependencies(利用依赖,或业务依赖)
  • devDependencies(开发环境依赖)
  • peerDependencies(等同依赖,或伙伴依赖)- 指定以后包兼容的宿主版本,如 gulp 插件
  • optionalDependencies(可选依赖)- 不阻断整体流程
  • bundledDependencies(打包依赖)- 蕴含依赖包名的数组对象,公布包时会打到最终的公布包外面
dependencies VS devDependencies

看一下下面的这个依赖关系图,你能说出哪些会被装置到 node_modules 么?

答案是:B、C、D、F

dependencies 和 devDependencies 的影响不是间接的,而是跨代的!参考文章

package-lock.json

曾经有了 package.json, 为什么会有 package-lock.json 文件呢?

我的项目依赖 A@1.0.0,A 依赖了 B@1.3.2 和 C@2.0.3

{
  "dependencies": {"A": "^1.0.0"}
}
  • 依赖 A 装置时下载了最新版,如 1.2.3,呈现了兼容问题,我的项目呈现 bug;
  • 依赖 A 所依赖的 BC 下载了别的版本,导致 A 呈现问题,从而我的项目呈现问题

作用:锁定装置时的包的版本号及包的依赖的版本号, 以保障其余所有人人在应用 ​​npm install​​ 时下载的依赖包都是统一的

对于 package.json 和 package-lock.json 的几个小论断:
  • package.json 用于通知 npm 我的项目运行须要哪些包, 但包的最终装置的版本不可能只依附这个文件进行辨认, 还需以 package-lock.json 为准
  • package.json 中批改版本号会影响 package-lock.json, 并且 package.json 比 package.lock.json 的优先级高
  • 为了保障该项目标环境依赖统一, 在我的项目挪动时须要同时复制 package.json 和 package.lock.json 两个文件
  • 不要轻易动 package.json 与 package-lock.json

删除重装一时爽,版本不对火葬场!!!

npm install 过程回顾

最初,咱们来看一下 npm install 的执行过程,来加深一下依赖治理的具体应用场景。


局部参考资料

  • 前端工程化 – 分析 npm 的包管理机制
  • 拿来吧您!把“前端依赖”纳入常识体系
  • npm — install 装置流程
  • 详解 package-lock.json 的作用
  • 版本校验工具
正文完
 0