初识 peerDependencies

目录

初步认识 peerDependencies

semver 介绍

# 摘要

# 简介

# 语义化版本控制规范(SemVer)

# 合法语义化版本的巴科斯范式语法

# 为什么要使用语义化的版本控制?

# FAQ

示例讲解:vue-router 插件

# 说明

声明

验证


初步认识 peerDependencies

在某些情况下,你想表达你的包与宿主工具或库的兼容性,而不一定要做这个宿主的 require。这通常被称为插件。值得注意的是,您的模块可能会暴露主机文档所期望和指定的特定接口。

例如:

{"name": "tea-latte","version": "1.3.5","peerDependencies": {"tea": "2.x"}
}

这确保您的包 tea-latte 只能与主机包 tea 的第二个主要版本一起安装。npm install tea-latte 可能会产生以下依赖关系图

├── tea-latte@1.3.5
└── tea@2.2.0

在 npm 版本 3 到 6 中,peerDependencies 不会自动安装,如果在树中发现无效版本的对等依赖项,则会发出警告。从 npm v7 开始,默认安装 peerDependencies。

如果无法正确解析树,则尝试安装另一个具有冲突要求的插件可能会导致错误。出于这个原因,请确保您的插件要求尽可能广泛,而不是将其锁定到特定的补丁版本。

假设主机符合 semver,只有主机包主要版本的更改会破坏您的插件。因此,如果您使用过每个 1.x 版本的主机包,请使用 "^1.0""1.x" 来表达这一点。如果您依赖 1.5.2 中引入的功能,请使用 "^1.5.2"

semver 介绍

# 摘要

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

  1. 主版本号:当你做了不兼容的 API 修改,
  2. 次版本号:当你做了向下兼容的功能性新增,
  3. 修订号:当你做了向下兼容的问题修正。

先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

# 简介

在软件管理的领域里存在着被称作“依赖地狱”的死亡之谷,系统规模越大,加入的包越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。

在依赖高的系统中发布新版本包可能很快会成为噩梦。如果依赖关系过高,可能面临版本控制被锁死的风险(必须对每一个依赖包改版才能完成某次升级)。而如果依赖关系过于松散,又将无法避免版本的混乱(假设兼容于未来的多个版本已超出了合理数量)。当你项目的进展因为版本依赖被锁死或版本混乱变得不够简便和可靠,就意味着你正处于依赖地狱之中。

作为这个问题的解决方案之一,我提议用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据(但不局限于)已经被各种封闭、开放源码软件所广泛使用的惯例所设计。为了让这套理论运作,你必须先有定义好的公共 API。这可能包括文档或代码的强制要求。无论如何,这套 API 的清楚明了是十分重要的。一旦你定义了公共 API,你就可以透过修改相应的版本号来向大家说明你的修改。考虑使用这样的版本号格式:X.Y.Z(主版本号.次版本号.修订号)修复问题但不影响 API 时,递增修订号;API 保持向下兼容的新增及修改时,递增次版本号;进行不向下兼容的修改时,递增主版本号。

我称这套系统为“语义化的版本控制”,在这套约定下,版本号及其更新方式包含了相邻版本间的底层代码和修改内容的信息。

# 语义化版本控制规范(SemVer)

以下关键词 MUST、MUST NOT、REQUIRED、SHALL、SHALL NOT、SHOULD、SHOULD NOT、 RECOMMENDED、MAY、OPTIONAL 依照 RFC 2119 的叙述解读。

  1. 使用语义化版本控制的软件必须(MUST)定义公共 API。该 API 可以在代码中被定义或出现于严谨的文档内。无论何种形式都应该力求精确且完整。
  2. 标准的版本号必须(MUST)采用 X.Y.Z 的格式,其中 X、Y 和 Z 为非负的整数,且禁止(MUST NOT)在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素必须(MUST)以数值来递增。例如:1.9.1 -> 1.10.0 -> 1.11.0。
  3. 标记版本号的软件发行后,禁止(MUST NOT)改变该版本软件的内容。任何修改都必须(MUST)以新版本发行。
  4. 主版本号为零(0.y.z)的软件处于开发初始阶段,一切都可能随时被改变。这样的公共 API 不应该被视为稳定版。
  5. 1.0.0 的版本号用于界定公共 API 的形成。这一版本之后所有的版本号更新都基于公共 API 及其修改内容。
  6. 修订号 Z(x.y.Z | x > 0)必须(MUST)在只做了向下兼容的修正时才递增。这里的修正指的是针对不正确结果而进行的内部修改。
  7. 次版本号 Y(x.Y.z | x > 0)必须(MUST)在有向下兼容的新功能出现时递增。在任何公共 API 的功能被标记为弃用时也必须(MUST)递增。也可以(MAY)在内部程序有大量新功能或改进被加入时递增,其中可以(MAY)包括修订级别的改变。每当次版本号递增时,修订号必须(MUST)归零。
  8. 主版本号 X(X.y.z | X > 0)必须(MUST)在有任何不兼容的修改被加入公共 API 时递增。其中可以(MAY)包括次版本号及修订级别的改变。每当主版本号递增时,次版本号和修订号必须(MUST)归零。
  9. 先行版本号可以(MAY)被标注在修订版之后,先加上一个连接号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。数字型的标识符禁止(MUST NOT)在前方补零。先行版的优先级低于相关联的标准版本。被标上先行版本号则表示这个版本并非稳定而且可能无法满足预期的兼容性需求。范例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。
  10. 版本编译信息可以(MAY)被标注在修订版或先行版本号之后,先加上一个加号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。当判断版本的优先层级时,版本编译信息可(SHOULD)被忽略。因此当两个版本只有在版本编译信息有差别时,属于相同的优先层级。范例:1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。
  11. 版本的优先层级指的是不同版本在排序时如何比较。

                1、判断优先层级时,必须(MUST)把版本依序拆分为主版本号、次版本号、修订号及先行版本号后进行比较(版本编译信息不在这份比较的列表中)。

                2、由左到右依序比较每个标识符,第一个差异值用来决定优先层级:主版本号、次版本号及修订号以数值比较。

                        例如:1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。

                3、当主版本号、次版本号及修订号都相同时,改以优先层级比较低的先行版本号决定。

                        例如:1.0.0-alpha < 1.0.0。

                4、有相同主版本号、次版本号及修订号的两个先行版本号,其优先层级必须(MUST)透过由左到右的每个被句点分隔的标识符来比较,直到找到一个差异值后决定:

                        a. 只有数字的标识符以数值高低比较。

                        b. 有字母或连接号时则逐字以 ASCII 的排序来比较。

                        c. 数字的标识符比非数字的标识符优先层级低。

                        d. 若开头的标识符都相同时,栏位比较多的先行版本号优先层级比较高。

                        例如:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 <                         1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0。

# 合法语义化版本的巴科斯范式语法

<valid semver> ::= <version core>| <version core> "-" <pre-release>| <version core> "+" <build>| <version core> "-" <pre-release> "+" <build><version core> ::= <major> "." <minor> "." <patch><major> ::= <numeric identifier><minor> ::= <numeric identifier><patch> ::= <numeric identifier><pre-release> ::= <dot-separated pre-release identifiers><dot-separated pre-release identifiers> ::= <pre-release identifier>| <pre-release identifier> "." <dot-separated pre-release identifiers><build> ::= <dot-separated build identifiers><dot-separated build identifiers> ::= <build identifier>| <build identifier> "." <dot-separated build identifiers><pre-release identifier> ::= <alphanumeric identifier>| <numeric identifier><build identifier> ::= <alphanumeric identifier>| <digits><alphanumeric identifier> ::= <non-digit>| <non-digit> <identifier characters>| <identifier characters> <non-digit>| <identifier characters> <non-digit> <identifier characters><numeric identifier> ::= "0"| <positive digit>| <positive digit> <digits><identifier characters> ::= <identifier character>| <identifier character> <identifier characters><identifier character> ::= <digit>| <non-digit><non-digit> ::= <letter>| "-"<digits> ::= <digit>| <digit> <digits><digit> ::= "0"| <positive digit><positive digit> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"<letter> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J"| "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T"| "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d"| "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n"| "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"| "y" | "z"


# 为什么要使用语义化的版本控制?

这并不是一个新的或者革命性的想法。实际上,你可能已经在做一些近似的事情了。问题在于只是“近似”还不够。如果没有某个正式的规范可循,版本号对于依赖的管理并无实质意义。将上述的想法命名并给予清楚的定义,让你对软件使用者传达意向变得容易。一旦这些意向变得清楚,弹性(但又不会太弹性)的依赖规范就能达成。

举个简单的例子就可以展示语义化的版本控制如何让依赖地狱成为过去。假设有个名为“救火车”的函数库,它需要另一个名为“梯子”并已经有使用语义化版本控制的包。当救火车创建时,梯子的版本号为 3.1.0。因为救火车使用了一些版本 3.1.0 所新增的功能,你可以放心地指定依赖于梯子的版本号大于等于 3.1.0 但小于 4.0.0。这样,当梯子版本 3.1.1 和 3.2.0 发布时,你可以将直接它们纳入你的包管理系统,因为它们能与原有依赖的软件兼容。

作为一位负责任的开发者,你理当确保每次包升级的运作与版本号的表述一致。现实世界是复杂的,我们除了提高警觉外能做的不多。你所能做的就是让语义化的版本控制为你提供一个健全的方式来发行以及升级包,而无需推出新的依赖包,节省你的时间及烦恼。

如果你对此认同,希望立即开始使用语义化版本控制,你只需声明你的函数库正在使用它并遵循这些规则就可以了。请在你的 README 文件中保留此页链接,让别人也知道这些规则并从中受益。

# FAQ

在 0.y.z 初始开发阶段,我该如何进行版本控制?
最简单的做法是以 0.1.0 作为你的初始化开发版本,并在后续的每次发行时递增次版本号。

如何判断发布 1.0.0 版本的时机?
当你的软件被用于正式环境,它应该已经达到了 1.0.0 版。如果你已经有个稳定的 API 被使用者依赖,也会是 1.0.0 版。如果你很担心向下兼容的问题,也应该算是 1.0.0 版了。

这不会阻碍快速开发和迭代吗?
主版本号为零的时候就是为了做快速开发。如果你每天都在改变 API,那么你应该仍在主版本号为零的阶段(0.y.z),或是正在下个主版本的独立开发分支中。

对于公共 API,若即使是最小但不向下兼容的改变都需要产生新的主版本号,岂不是很快就达到 42.0.0 版?
这是开发的责任感和前瞻性的问题。不兼容的改变不应该轻易被加入到有许多依赖代码的软件中。升级所付出的代价可能是巨大的。要递增主版本号来发行不兼容的改版,意味着你必须为这些改变所带来的影响深思熟虑,并且评估所涉及的成本及效益比。

为整个公共 API 写文档太费事了!
为供他人使用的软件编写适当的文档,是你作为一名专业开发者应尽的职责。保持项目高效的一个非常重要的部分是掌控软件的复杂度,如果没有人知道如何使用你的软件或不知道哪些函数的调用是可靠的,要掌控复杂度会是困难的。长远来看,使用语义化版本控制以及对于公共 API 有良好规范的坚持,可以让每个人及每件事都运行顺畅。

万一不小心把一个不兼容的改版当成了次版本号发行了该怎么办?
一旦发现自己破坏了语义化版本控制的规范,就要修正这个问题,并发行一个新的次版本号来更正这个问题并且恢复向下兼容。即使是这种情况,也不能去修改已发行的版本。可以的话,将有问题的版本号记录到文档中,告诉使用者问题所在,让他们能够意识到这是有问题的版本。

如果我更新了自己的依赖但没有改变公共 API 该怎么办?
由于没有影响到公共 API,这可以被认定是兼容的。若某个软件和你的包有共同依赖,则它会有自己的依赖规范,作者也会告知可能的冲突。要判断改版是属于修订等级或是次版等级,是依据你更新的依赖关系是为了修复问题或是加入新功能。对于后者,我经常会预期伴随着更多的代码,这显然会是一个次版本号级别的递增。

如果我变更了公共 API 但无意中未遵循版本号的改动怎么办呢?(意即在修订等级的发布中,误将重大且不兼容的改变加到代码之中)
自行做最佳的判断。如果你有庞大的使用者群在依照公共 API 的意图而变更行为后会大受影响,那么最好做一次主版本的发布,即使严格来说这个修复仅是修订等级的发布。记住, 语义化的版本控制就是透过版本号的改变来传达意义。若这些改变对你的使用者是重要的,那就透过版本号来向他们说明。

我该如何处理即将弃用的功能?
弃用现存的功能是软件开发中的家常便饭,也通常是向前发展所必须的。当你弃用部分公共 API 时,你应该做两件事:(1)更新你的文档让使用者知道这个改变,(2)在适当的时机将弃用的功能透过新的次版本号发布。在新的主版本完全移除弃用功能前,至少要有一个次版本包含这个弃用信息,这样使用者才能平顺地转移到新版 API。

语义化版本对于版本的字符串长度是否有限制呢?
没有,请自行做适当的判断。举例来说,长到 255 个字符的版本已过度夸张。再者,特定的系统对于字符串长度可能会有他们自己的限制。

“v1.2.3” 是一个语义化版本号吗?
“v1.2.3” 并不是一个语义化的版本号。但是,在语义化版本号之前增加前缀 “v” 是用来表示版本号的常用做法。在版本控制系统中,将 “version” 缩写为 “v” 是很常见的。比如:git tag v1.2.3 -m "Release version 1.2.3" 中,“v1.2.3” 表示标签名称,而 “1.2.3” 是语义化版本号。

是否有推荐的正则表达式用以检查语义化版本号的正确性?
有两个推荐的正则表达式。第一个用于支持按组名称提取的语言(PCRE[Perl 兼容正则表达式,比如 Perl、PHP 和 R]、Python 和 Go)。

参见:https://regex101.com/r/Ly7O1x/3/

^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$


第二个用于支持按编号提取的语言(与第一个对应的提取项按顺序分别为:major、minor、patch、prerelease、buildmetadata)。主要包括 ECMA Script(JavaScript)、PCRE(Perl 兼容正则表达式,比如 Perl、PHP 和 R)、Python 和 Go。 参见:https://regex101.com/r/vkijKf/1/

^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*

示例讲解:vue-router 插件

贴上vue-router插件的package.json代码,可以看到其中 peerDependencies 值为 {"vue": "^3.2.0"}

{"name": "vue-router","version": "4.3.2","main": "index.js","unpkg": "dist/vue-router.global.js","jsdelivr": "dist/vue-router.global.js","module": "dist/vue-router.mjs","types": "dist/vue-router.d.ts","exports": {"./auto-routes": {"types": "./vue-router-auto-routes.d.ts","node": {"import": {"production": "./dist/vue-router.node.mjs","development": "./dist/vue-router.node.mjs","default": "./dist/vue-router.node.mjs"},"require": {"production": "./dist/vue-router.prod.cjs","development": "./dist/vue-router.cjs","default": "./index.js"}},"import": "./dist/vue-router.mjs","require": "./index.js"},"./auto": {"types": "./vue-router-auto.d.ts","node": {"import": {"production": "./dist/vue-router.node.mjs","development": "./dist/vue-router.node.mjs","default": "./dist/vue-router.node.mjs"},"require": {"production": "./dist/vue-router.prod.cjs","development": "./dist/vue-router.cjs","default": "./index.js"}},"import": "./dist/vue-router.mjs","require": "./index.js"},".": {"types": "./dist/vue-router.d.ts","node": {"import": {"production": "./dist/vue-router.node.mjs","development": "./dist/vue-router.node.mjs","default": "./dist/vue-router.node.mjs"},"require": {"production": "./dist/vue-router.prod.cjs","development": "./dist/vue-router.cjs","default": "./index.js"}},"import": "./dist/vue-router.mjs","require": "./index.js"},"./dist/*": "./dist/*","./vetur/*": "./vetur/*","./package.json": "./package.json"},"sideEffects": false,"author": {"name": "Eduardo San Martin Morote","email": "posva13@gmail.com"},"funding": "https://github.com/sponsors/posva","license": "MIT","repository": {"type": "git","url": "git+https://github.com/vuejs/router.git"},"bugs": {"url": "https://github.com/vuejs/router/issues"},"homepage": "https://github.com/vuejs/router#readme","files": ["index.js","dist/*.{js,cjs,mjs}","dist/vue-router.d.ts","vue-router-auto.d.ts","vue-router-auto-routes.d.ts","vetur/tags.json","vetur/attributes.json","README.md"],"peerDependencies": {"vue": "^3.2.0"},"vetur": {"tags": "vetur/tags.json","attributes": "vetur/attributes.json"},"dependencies": {"@vue/devtools-api": "^6.5.1"},"devDependencies": {"@microsoft/api-extractor": "^7.40.1","@rollup/plugin-alias": "^5.1.0","@rollup/plugin-commonjs": "^25.0.7","@rollup/plugin-node-resolve": "^15.2.3","@rollup/plugin-replace": "^5.0.5","@rollup/plugin-terser": "^0.4.4","@sucrase/jest-plugin": "^3.0.0","@types/jest": "^29.5.12","@types/jsdom": "^21.1.6","@types/nightwatch": "^2.3.30","@vitejs/plugin-vue": "^5.0.4","@vue/compiler-sfc": "^3.4.23","@vue/server-renderer": "^3.4.23","@vue/test-utils": "^2.4.4","browserstack-local": "^1.5.5","chromedriver": "^121.0.2","connect-history-api-fallback": "^1.6.0","conventional-changelog-cli": "^2.1.1","dotenv": "^16.4.5","faked-promise": "^2.2.2","geckodriver": "^3.2.0","jest": "^29.7.0","jest-environment-jsdom": "^29.7.0","jest-mock-warn": "^1.1.0","nightwatch": "^2.6.22","nightwatch-helpers": "^1.2.0","rimraf": "^5.0.5","rollup": "^3.29.4","rollup-plugin-analyzer": "^4.0.0","rollup-plugin-typescript2": "^0.36.0","sucrase": "^3.34.0","typescript": "~5.3.3","vite": "^5.2.9","vue": "^3.4.23"},"scripts": {"dev": "jest --watch","changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1","build": "rimraf dist && rollup -c rollup.config.mjs","build:dts": "api-extractor run --local --verbose && tail -n +10 src/globalExtensions.ts >> dist/vue-router.d.ts","build:playground": "vue-tsc --noEmit && vite build --config playground/vite.config.ts","build:e2e": "vue-tsc --noEmit && vite build --config e2e/vite.config.mjs","build:size": "pnpm run build && rollup -c size-checks/rollup.config.mjs","dev:e2e": "vite --config e2e/vite.config.mjs","test:types": "tsc --build tsconfig.json","test:dts": "tsc -p ./test-dts/tsconfig.json","test:unit": "jest --coverage","test": "pnpm run test:types && pnpm run test:unit && pnpm run build && pnpm run build:dts && pnpm run test:e2e","test:e2e": "pnpm run test:e2e:headless","test:e2e:headless": "node e2e/runner.mjs --env chrome-headless","test:e2e:native": "node e2e/runner.mjs --env chrome","test:e2e:ci": "node e2e/runner.mjs --env chrome-headless --retries 2","test:e2e:bs": "node e2e/runner.mjs --local -e android5 --tag browserstack","test:e2e:bs-test": "node e2e/runner.mjs --local --env browserstack.local_chrome --tag browserstack"}
}

# 说明

vue-router在开发过程中依赖于vue包,但是又不希望将这个依赖关系包含在自己的发布版本中时,就可以使用 peerDependencies(同行依赖)。

让我更详细地解释一下 peerDependencies 的概念和用法:

1. 什么是 peerDependencies?

peerDependencies 是指一个软件包在运行时所需的依赖项,但是它希望这些依赖项由使用它的其他软件包提供,而不是自己将这些依赖项包含在自己的发布版本中。

2. 为什么使用 peerDependencies?

使用 peerDependencies 的主要目的是为了避免在一个项目中出现依赖项的重复版本。当一个软件包同时依赖于另一个软件包,并且两者都在自己的发布版本中包含了相同版本的依赖项时,这可能会导致冲突和版本不一致的问题。

通过将依赖项声明为 peerDependencies,软件包作者可以明确指定它的运行时依赖应该由使用它的其他软件包提供,从而确保所有使用者都使用同一版本的依赖项,避免了版本冲突和不一致的问题。

3. 如何声明 peerDependencies?

要声明 peerDependencies,您可以将依赖项添加到您的软件包的 package.json 文件中的 peerDependencies 字段中,例如:

{"name": "your-package","peerDependencies": {"dependency-name": "^1.0.0"}
}

在上面的示例中,dependency-name 是您的软件包在运行时所需的同行依赖项的名称,^1.0.0 表示您的软件包兼容的依赖项的版本范围。

4. 使用 peerDependencies 的注意事项:

  • 使用者需要手动安装 peerDependencies。在使用您的软件包时,使用者需要确保安装了您声明的 peerDependencies。通常,npm 和 Yarn 在安装软件包时会自动安装相应的 peerDependencies,但是如果使用者手动管理依赖项或者使用其他包管理工具,则可能需要手动安装。
  • 确保对peerDependencies 的正确使用。在声明 peerDependencies 时,需要确保使用者确实能够提供所需的依赖项。通常情况下,应该将 peerDependencies 声明为您的软件包所需要的最小版本,以确保与使用者的兼容性。
  • 避免滥用 peerDependencies。尽管 peerDependencies 可以解决依赖项重复版本的问题,但是滥用会导致依赖项的管理变得复杂,并且可能会造成一些意想不到的问题。因此,在声明 peerDependencies 时应该谨慎选择,并且仅在确实需要时使用。

总的来说,peerDependencies 是一种在软件包开发中管理依赖项的重要机制,可以帮助开发者避免版本冲突和不一致的问题,同时提高了软件包的灵活性和可重用性。

声明

要在您的项目中声明对 vue 3.4.27 版本的 peerDependencies,您可以通过以下步骤完成:

1. 编辑 package.json:

打开您的项目的 package.json 文件,并添加或更新 peerDependencies 字段,如下所示:

{"name": "your-project","version": "1.0.0","peerDependencies": {"vue": "^3.4.27"}
}


2. 安装 Vue:

然后,在您的项目中安装 Vue,确保安装的版本符合您在 peerDependencies 中声明的范围。您可以运行以下命令:

npm install vue@3.4.27 --save-peer

或者,如果您使用的是 Yarn:

yarn add vue@3.4.27 --peer


3. 更新依赖:

安装 Vue 后,您可能需要运行 npm installyarn 命令来更新您的项目的依赖项。

4. 验证配置:

最后,您可以确保 package.json 中的 peerDependencies 部分以及您安装的 Vue 版本符合预期。您可以通过查看 package.json 文件和运行 npm list vueyarn list vue 命令来验证。

这样,您的项目就声明了对 Vue 3.4.27 版本的 peerDependencies。任何使用您的项目的其他软件包都应该在运行时提供符合这个版本要求的 Vue。

验证

这里我随意打开了一个之前项目,查看了vue的同行依赖关系

PS D:\vinca\web> npm list vue
nuxt-app@2.0LTS D:\vinca\web   
├─┬ @chenfengyuan/vue-qrcode@2.0.0-rc.1
│ └── vue@3.4.21 deduped
├─┬ @element-plus/icons-vue@2.3.1      
│ └── vue@3.4.21 deduped
├─┬ @nuxtjs/seo@2.0.0-rc.10
│ └─┬ nuxt-link-checker@3.0.0-rc.7     
│   ├─┬ @vueuse/core@10.9.0
│   │ ├─┬ @vueuse/shared@10.9.0        
│   │ │ └─┬ vue-demi@0.14.7
│   │ │   └── vue@3.4.21 deduped       
│   │ └─┬ vue-demi@0.14.7
│   │   └── vue@3.4.21 deduped
│   ├─┬ floating-vue@5.2.0
│   │ └── vue@3.4.21 deduped
│   ├─┬ nuxt-site-config-kit@2.2.7
│   │ └─┬ site-config-stack@2.2.7
│   │   └── vue@3.4.21 deduped
│   └─┬ nuxt-site-config@2.2.7
│     └─┬ site-config-stack@2.2.7
│       └── vue@3.4.21 deduped
├─┬ @nuxtjs/sitemap@5.1.4
│ ├─┬ @nuxt/devtools-ui-kit@1.1.5
│ │ ├─┬ @vueuse/core@10.9.0
│ │ │ └─┬ vue-demi@0.14.7
│ │ │   └── vue@3.4.21 deduped
│ │ ├─┬ @vueuse/integrations@10.9.0
│ │ │ └─┬ vue-demi@0.14.7
│ │ │   └── vue@3.4.21 deduped
│ │ └─┬ @vueuse/nuxt@10.9.0
│ │   └─┬ vue-demi@0.14.7
│ │     └── vue@3.4.21 deduped
│ ├─┬ @vueuse/core@10.9.0
│ │ └─┬ vue-demi@0.14.7
│ │   └── vue@3.4.21 deduped
│ ├─┬ floating-vue@5.2.2
│ │ ├─┬ vue-resize@2.0.0-alpha.1
│ │ │ └── vue@3.4.21 deduped
│ │ └── vue@3.4.21 deduped
│ └─┬ site-config-stack@2.2.12
│   └── vue@3.4.21 deduped
├─┬ element-plus@2.6.3
│ ├─┬ @vueuse/core@9.13.0
│ │ ├─┬ @vueuse/shared@9.13.0
│ │ │ └─┬ vue-demi@0.14.7
│ │ │   └── vue@3.4.21 deduped
│ │ └─┬ vue-demi@0.14.7
│ │   └── vue@3.4.21 deduped
│ └── vue@3.4.21 deduped
├─┬ nuxt-og-image@3.0.0-rc.53
│ ├─┬ @vueuse/core@10.9.0
│ │ └─┬ vue-demi@0.14.7
│ │   └── vue@3.4.21 deduped
│ ├─┬ json-editor-vue@0.15.1
│ │ └── vue@3.4.21 deduped
│ └─┬ nuxt-icon@0.6.10
│   └─┬ @iconify/vue@4.1.1
│     └── vue@3.4.21 deduped
├─┬ nuxt-svgo@4.0.0
│ └── vue@3.4.21 deduped
├─┬ nuxt@3.10.3
│ ├─┬ @nuxt/devtools@1.1.5
│ │ ├─┬ @vue/devtools-applet@7.0.27
│ │ │ ├─┬ @vue/devtools-ui@7.0.27
│ │ │ │ ├─┬ @vueuse/components@10.9.0
│ │ │ │ │ └─┬ vue-demi@0.14.7
│ │ │ │ │   └── vue@3.4.21 deduped
│ │ │ │ ├─┬ @vueuse/core@10.9.0
│ │ │ │ │ ├─┬ @vueuse/shared@10.9.0
│ │ │ │ │ │ └─┬ vue-demi@0.14.7
│ │ │ │ │ │   └── vue@3.4.21 deduped
│ │ │ │ │ └─┬ vue-demi@0.14.7
│ │ │ │ │   └── vue@3.4.21 deduped
│ │ │ │ └── vue@3.4.21 deduped
│ │ │ ├─┬ vue-virtual-scroller@2.0.0-beta.8
│ │ │ │ ├─┬ vue-observe-visibility@2.0.0-alpha.1
│ │ │ │ │ └── vue@3.4.21 deduped
│ │ │ │ └── vue@3.4.21 deduped
│ │ │ └── vue@3.4.21 deduped
│ │ └─┬ @vue/devtools-kit@7.0.27
│ │   └── vue@3.4.21 deduped
│ ├─┬ @nuxt/vite-builder@3.10.3
│ │ ├─┬ @vitejs/plugin-vue-jsx@3.1.0
│ │ │ └── vue@3.4.21 deduped
│ │ ├─┬ @vitejs/plugin-vue@5.0.4
│ │ │ └── vue@3.4.21 deduped
│ │ └── vue@3.4.21 deduped
│ ├─┬ @unhead/vue@1.8.18
│ │ └── vue@3.4.21 deduped
│ ├─┬ unplugin-vue-router@0.7.0
│ │ └─┬ @vue-macros/common@1.10.1
│ │   └── vue@3.4.21 deduped
│ └── vue@3.4.21 deduped
├─┬ pinia@2.1.7
│ ├─┬ vue-demi@0.14.7
│ │ └── vue@3.4.21 deduped
│ └── vue@3.4.21 deduped
├─┬ vue-router@4.3.0
│ └── vue@3.4.21 deduped
└─┬ vue@3.4.21└─┬ @vue/server-renderer@3.4.21└── vue@3.4.21 deduped

在 npm 的依赖关系图中,deduped 表示已经被去重了。这意味着在整个依赖树中,有两个或多个依赖项指向了同一个版本的软件包。当 npm 发现这种情况时,它会去除重复的依赖项,只保留一个,并在其他依赖项中引用这个已去重的依赖项。

这种去重机制有助于减少依赖项的重复,减小项目的体积,同时确保所有依赖项都使用同一版本的软件包,从而避免版本冲突和其他问题。


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

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

相关文章

Hive知识体系保姆级教程

一. Hive概览 1.1 hive的简介 Hive是基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张数据库表&#xff0c;并提供类SQL查询功能。 其本质是将SQL转换为MapReduce/Spark的任务进行运算&#xff0c;底层由HDFS来提供数据的存储&#xff0c;说白了h…

野火FPGA跟练(四)——串口RS232、亚稳态

目录 简介接口与引脚通信协议亚稳态RS232接收模块模块框图时序波形RTL 代码易错点Testbench 代码仿真 RS232发送模块模块框图时序波形RTL 代码Testbench 代码仿真 简介 UART&#xff1a;Universal Asynchronous Receiver/Transmitter&#xff0c;异步串行通信接口。发送数据时…

python机器人编程@我从0开始搭建了一个色块自动抓取机器人,并实现了大模型的接入和语音控制——(上基础篇)

目录 1、前言2、仿真环境的搭建3、仿真环境资源链接4、总结1、前言 在现代社会中,机器人技术正变得越来越普遍和重要。机器人的自动化能力使其能够在许多不同的领域发挥作用,例如生产线、医疗保健和家庭助理等。本系列将着重介绍如何搭建一个自动色块抓取机器人。 首先,我…

四种跨域解决方案

文章目录 1.引出跨域1.基本介绍2.具体演示1.启动之前学习过的springboot-furn项目2.浏览器直接访问 [localhost:8081/furns](http://localhost:8081/furns) 可以显示信息3.启动前端项目&#xff0c;取消请求拦截器&#xff0c;这样设置&#xff0c;就会出现跨域4.跨域原因 2.跨…

【小白专用24.6.8】C# 异步任务Task和异步方法async/await详解

一、什么是异步 同步和异步主要用于修饰方法。当一个方法被调用时&#xff0c;调用者需要等待该方法执行完毕并返回才能继续执行&#xff0c;我们称这个方法是同步方法&#xff1b;当一个方法被调用时立即返回&#xff0c;并获取一个线程执行该方法内部的业务&#xff0c;调用…

电脑缺失msvcp110.dll文件的解决方法,总结5种靠谱的方法

在计算机使用过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“找不到msvcp110.dll”。这个错误提示通常出现在运行某些软件时&#xff0c;那么&#xff0c;它究竟会造成哪些问题呢&#xff1f; 一&#xff0c;msvcp110.dll文件概述 msvcp110.dll是Mic…

实验八、地址解析协议《计算机网络》

水逆退散&#xff0c;学业进步&#xff0c;祝我们都好&#xff0c;不止在夏天。 目录 一、实验目的 二、实验内容 &#xff08;1&#xff09;预备知识 &#xff08;2&#xff09;实验步骤 三、实验小结 一、实验目的 完成本练习之后&#xff0c;您应该能够确定给定 IP 地…

WEB-Wordlist-Generator:为扫描后的Web应用生成相关联的字典

关于WEB-Wordlist-Generator WEB-Wordlist-Generator是一款功能强大的字典生成工具&#xff0c;该工具旨在帮助广大研究人员扫描目标Web应用程序并生成与之相关联的字典文件&#xff0c;从而允许我们对相关的网络威胁行为执行预备性应对策略。 功能介绍 当前版本的WEB-Wordli…

区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测

区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测 目录 区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…

计算机组成原理之计算机系统层次结构

目录 计算机系统层次结构 复习提示 1.计算机系统的组成 2.计算机硬件 2.1冯诺依曼机基本思想 2.1.1冯诺依曼计算机的特点 2.2计算机的功能部件 2.2.1MAR 和 MDR 位数的概念和计算 3.计算机软件 3.1系统软件和应用软件 3.2三个级别的语言 3.2.1三种机器语言的特点 3…

如何将 Windows图片查看器的背景颜色改成浅色(灰白色)?

现在大家基本都在使用Win10系统&#xff0c;我们在双击查看图片时&#xff0c;系统默认使用系统自带的图片&#xff08;照片&#xff09;查看器去打开图片。图片查看器的背景色默认是黑色的&#xff0c;如下所示&#xff1a;&#xff08;因为大家可能会遇到同样的问题&#xff…

【报文数据流中的反压处理】

报文数据流中的反压处理 1 带存储体的反压1.1 原理图1.2 Demo 尤其是在NP芯片中&#xff0c;经常涉及到报文的数据流处理&#xff1b;为了防止数据丢失&#xff0c;和各模块的流水处理&#xff1b;因此需要到反压机制&#xff1b; 反压机制目前接触到的有两种&#xff1a;一是基…

接口自动化Requests+Pytest基础实现

目录 1. 数据库以及数据库操作1.1 概念1.2 分类1.3 作用 2 python操作数据库的相关实现2.1 背景2.2 相关实现 3. pymysql基础3.1 整个流程3.2 案例3.3 Pymysql工具类封装 4 事务4.1 案例4.2 事务概念4.3 事务特征 5. requests库5.1 概念5.2 角色定位5.3 安装5.4 校验5.5 reques…

吊车报警的工作原理和使用场景_鼎跃安全

在现代建筑施工过程中&#xff0c;经常使用大型机械设备&#xff0c;如挖掘机、吊车、打桩机等&#xff0c;这些设备在施工过程中发挥着越来越重要的作用&#xff1b;同时&#xff0c;这些设备的作业频繁进行作业&#xff0c;对于接触到高压电线的风险也随之增加。大型机械设备…

Application Load Balancer-ALB

Application Load Balancer-ALB 什么是ALB开通ALB服务实现IPv4服务的负载均衡创建ALB实例创建服务器组添加后端服务器配置监听设置域名解析&#xff08;可选&#xff09;释放ALB实例 什么是ALB 在介绍ALB之前首先介绍一下负载均衡SLB&#xff0c;可以说SLB是负载均衡家族之首 …

测试开发之自动化篇 —— 使用Selenium IDE录制脚本!

今天&#xff0c;我们开始介绍基于开源Selenium工具的Web网站自动化测试。 Selenium包含了3大组件&#xff0c;分别为&#xff1a;1. Selenium IDE 基于Chrome和Firefox扩展的集成开发环境&#xff0c;可以录制、回放和导出不同语言的测试脚本。 2. WebDriver 包括一组为不同…

Adobe Illustrator 矢量图设计软件下载安装,Illustrator 轻松创建各种矢量图形

Adobe Illustrator&#xff0c;它不仅仅是一个简单的图形编辑工具&#xff0c;更是一个拥有丰富功能和强大性能的设计利器。 在这款软件中&#xff0c;用户可以通过各种精心设计的工具&#xff0c;轻松创建和编辑基于矢量路径的图形文件。这些矢量图形不仅具有高度的可编辑性&a…

“深入探讨Java中的对象拷贝:浅拷贝与深拷贝的差异与应用“

前言&#xff1a;在Java编程中&#xff0c;深拷贝&#xff08;Deep Copy&#xff09;与浅拷贝&#xff08;Shallow Copy&#xff09;是两个非常重要的概念。它们涉及到对象在内存中的复制方式&#xff0c;对于理解对象的引用、内存管理以及数据安全都至关重要。 ✨✨✨这里是秋…

springboot undertow 文件上传文件过大异常

io.undertow.server.RequestTooBigException: UT000020 Connection terminated as request was larger than xxxx 修改yaml文件中关于undertow的配置项 server:undertow:# HTTP POST请求最大的大小# 默认0&#xff0c;无限制max-http-post-size: ${SERVER_UNDERTOW_MAX_HTTP_…

小白教程--- kali(po解)WIFI密码 (图文教程)

kali学得好&#xff0c;牢饭少不了&#xff01;&#xff01;&#xff01; 原理&#xff1a; 模拟WiFi的已连接设备&#xff0c;强制让其下线重连&#xff0c;获取其握手包&#xff0c;使用密码字典&#xff08;宝丽&#xff09;婆洁。 环境&#xff08;准备工作&#xff09;&a…