每次我们运行一个新的 Flutter 项目并运行它时,我们都会看到一条消息:
1 |
"flutter pub get" in a_flutter_project... |
问题是:当我们获得新的依赖项时会发生什么?
正如我们在上一篇文章中看到的《深入研究 Flutter Pubspec.yaml 文件》我们有不同的方法来向项目添加依赖项,但我们还没有讨论的一件事是为什么我们有时会添加插入^
符号在我们的依赖版本之前:
1 2 |
# ... flutter_bloc: ^7.0.1 |
这个符号有什么作用?它与pubspec.lock
文件有什么关系?
灵活的依赖版本
正如Dart官方文档中所见,有几种不同的方式来定义包的版本:
-
any
或为空 - 这将导致包版本被定义为最新版本或确定相对于其他包约束应该使用哪个版本 -
<
,>
,<=
,>=
- 允许我们选择更低、更高、更低且等于或更高且等于规定版本的版本
但是,如果我们想要有灵活性和一些稳定性的保证,我们应该考虑使用插入^
符号。如文档中所定义:
^version
表示/保证向后兼容指定版本的所有版本的范围。
因此,遵循语义版本指南(其中1.2.3
是Major 1、Minor 2 和Patch 3),这意味着:
-
对于没有主要版本的依赖项,例如
0.12.3
和0.0.2->
插入符号将查找包含在同一次要版本中的依赖项。所以^0.12.3
会满足所有版本>= 0.12.3 < 0.13.0
。 -
对于具有主要版本的依赖项,例如
1.2.3
,插入符号将查找包含在同一主要版本中的依赖项。所以^1.2.3
会满足所有版本:>=1.2.3 <2.0.0
理论上(因为在实践中一些库可以打破这个规则),我们不会有从to或 from to 的破坏性更改,这就是为什么 Dart 冒昧地为我们升级这些版本。0.12.30.12.61.2.31.6.90
现在,这引出了两个不同的问题:
- Dart 什么时候更新我们的依赖版本?
- 它如何为项目选择合适的版本?
- 在哪里可以找到与应用程序一起使用的特定版本?
要回答这些问题,我们必须了解什么是pubspec.lock
文件。
pubspec.lock文件的剖析
与其尝试从整体上查看文件,不如换一种方式——我们创建一个新项目,提交它,然后添加一个新的依赖项:
1 2 3 |
dependencies: #... version_banner: ^0.2.0 |
之后,我们运行flutter pub get
并查看pubspec.lock
文件的差异内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
diff --git a/pubspec.lock b/pubspec.lock index 640db00..7d62228 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,6 +81,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + package_info: + dependency: transitive + description: + name: package_info + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.3+4" path: dependency: transitive description: @@ -149,5 +156,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + version_banner: + dependency: "direct main" + description: + name: version_banner + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" sdks: dart: ">=2.12.0 <3.0.0" + flutter: ">=1.12.13+hotfix.5" |
我们一点一点来分析一下:
1 2 3 4 5 6 7 |
version_banner: dependency: "direct main" description: name: version_banner url: "https://pub.dartlang.org" source: hosted version: "0.2.1" |
- 在顶部,我们有库名称定义,在本例中为
version_banner
; - 接着是
dependency
类型,它告诉我们它是一个direct
依赖项,即我们通过pubspec.yaml
文件添加的,还是一个transitive
依赖项(如package_info
锁定文件中的依赖项所示),它是一个依赖项的依赖项; - 在
description
将相关的依赖性的类型的其他信息(GIT,本地或托管),无论是路径依赖,在git
的承诺散列和URL存储库,或托管依赖的URL,在这种情况下, ,pub.dev
; source
告诉我们如何将依赖项添加到项目中。在本例中,我们通过pub.dev
(托管)添加它,但我们也可以使用sourceaspath
或git
;- 最后,
version
将告诉我们我们正在使用的具体版本是什么。
如果我们仔细查看version
,我们会发现虽然我们在pubspec.yamlversion
中定义了^0.2.0
,但从锁定文件中解析出来的版本是0.2.1
,这意味着 Dart
能够接受并使用最新版本的库,尊重它的次要版本。
为什么我们应该使用^
如果我们只使用一个依赖,使用的好处^
只是让我们拥有最新最好的版本。然而,^
在我们的依赖项中使用还有另一个很好的理由——它允许 Dart
足够灵活地选择一个与所有瞬态(或依赖项的依赖项)依赖项一致的依赖项版本。
想象一下以下场景 - 您http在应用程序中使用最新版本的包 - http: 0.13.0.
但是,您还使用了另一个名为 library_b
的库,它也依赖于http
,但它使用的是更新版本!http: 0.13.3.
1 2 3 4 5 6 7 8 |
name: app_a # ... dependencies: library_b: path: ../library_b http: 0.13.0 |
1 2 3 4 5 |
name: library_b version: 0.0.1 # ... dependencies: http: 0.13.3 |
那么如果我们选择使用确切的版本会发生什么http: 0.13.3
?
1 2 3 4 5 6 |
➜ app_a flutter pub get Running "flutter pub get" in app_a... Because app_a depends on library_b from path which depends on http 0.13.3, http 0.13.3 is required. So, because app_a depends on http 0.13.0, version solving failed. pub get failed (1; So, because app_a depends on http 0.13.0, version solving failed.) |
由于我们使用的是硬依赖版本,Dart
将无法找到同时满足这两个约束的版本。但是如果我们添加^到应用程序的依赖项中呢?从理论上讲,这意味着应用程序会说“我可以有一个灵活的 http 版本,所以找到一个既满足又不引起冲突的版本”。
1 2 3 4 5 6 7 8 |
name: app_a # ... dependencies: library_b: path: ../library_b http: ^0.13.3 |
就这样,我们运行flutter pub get
命令:
1 2 |
➜ app_a flutter pub get Running "flutter pub get" in app_a... |
查看pubspec.lock
应用程序的文件,我们看到它现在使用更新版本0.13.3
:
1 2 3 4 5 6 7 |
http: dependency: "direct main" description: name: http url: "https://pub.dartlang.org" source: hosted version: "0.13.3" |
但是,如果版本不同会发生什么?让我们看看http应用程序版本高于库版本的情况:
1 2 3 4 5 6 7 8 |
name: app_a # ... dependencies: library_b: path: ../library_b http: ^0.13.3 |
1 2 3 4 5 |
name: library_b version: 0.0.1 # ... dependencies: http: 0.13.0 |
该^符号只能找到等于或高于我们声明的版本的版本,这意味着如果我们运行,flutter pub get
我们将看到以下错误消息:
1 2 3 4 5 6 |
➜ app_a flutter pub get Running "flutter pub get" in app_a... Because app_a depends on library_b from path which depends on http 0.13.0, http 0.13.0 is required. Running "flutter pub get" in app_a... So, because app_a depends on http ^0.13.3, version solving failed. Running "flutter pub get" in app_a... pub get failed (1; So, because app_a depends on http ^0.13.3, version solving failed.) |
解决这个问题的一个唯一的办法就是让这两个库和应用程序依赖于一个^版本,让他们找到一个版本,同时满足。
如果我们无法^在我们正在使用的依赖项中添加表示法,我们总是可以dependency_overrides
在我们的pubspec.yaml
文件中使用 a ,正如我们在深入研究 Pubspec.yaml
文件中所讨论的那样。
升级我们的版本
因此,如果通过运行flutter pub get
我们没有看到通过pubspec.lock
文件对我们的包进行任何更新,那么我们如何更新我们的依赖项?
答案是使用flutter pub upgrade
!
让我们以以下应用程序依赖项为例,并假设pubspec.lock
文件使用相同的版本:
1 2 3 4 5 6 |
name: app_a # ... dependencies: http: ^0.13.0 flutter_bloc: ^7.0.0 |
如果我们运行,flutter pub upgrade
我们会看到所有依赖项都将具有更高版本,符合以下标准^
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
➜ app_a git:(master) flutter pub upgrade Resolving dependencies... bloc 7.0.0 characters 1.1.0 collection 1.15.0 ffi 1.1.2 file 6.1.2 flutter 0.0.0 from sdk flutter > flutter_bloc 7.1.0 (was 7.0.0) > shared_preferences 2.0.6 (was 2.0.0) ... Changed 2 dependencies! 1 package has newer versions incompatible with dependency constraints. Try `flutter pub outdated` for more information. |
通过查看差异,我们可以看到这些版本已更新到较新的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@@ -47,7 +47,7 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "7.0.0" + version: "7.1.0" flutter_web_plugins: dependency: transitive description: flutter @@ -136,7 +136,7 @@ packages: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.6" shared_preferences_linux: dependency: transitive description: |
但是如果我们不想升级所有的依赖呢?如果我们只想测试特定包的最新版本怎么办?幸运的是,该upgrade
命令允许我们使用flutter pub upgrade <package_name>
.
让我们通过使用来测试它flutter pub upgrade shared_preferences
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
➜ app_a git:(master) flutter pub upgrade shared_preferences Resolving dependencies... bloc 7.0.0 characters 1.1.0 collection 1.15.0 ffi 1.1.2 file 6.1.2 flutter 0.0.0 from sdk flutter flutter_bloc 7.0.0 (7.1.0 available) > shared_preferences 2.0.6 (was 2.0.0) ... Changed 1 dependency! 2 packages have newer versions incompatible with dependency constraints. Try `flutter pub outdated` for more information. |
正如我们所看到的,命令行告诉我们,虽然有一个更新的版本可用于flutter_bloc
,但它没有被应用。我们可以通过查看差异来验证这一点:
1 2 3 4 5 6 7 8 9 |
@@ -136,7 +136,7 @@ packages: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.6" shared_preferences_linux: dependency: transitive description: |
查看flutter pub upgrade
命令的最后一行,我们看到它提到了该flutter pub outdated
命令。如果我们希望pubspec.yaml
直接更新我们的文件,此命令可用于检查我们软件包的所有最新和兼容版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
➜ app_a git:(master) ✗ flutter pub outdated Showing outdated packages. [*] indicates versions that are not the latest available. Package Name Current Upgradable Resolvable Latest direct dependencies: flutter_bloc *7.0.0 7.1.0 7.1.0 7.1.0 shared_preferences *2.0.0 2.0.6 2.0.6 2.0.6 transitive dependencies: meta *1.3.0 *1.3.0 *1.3.0 1.7.0 2 upgradable dependencies are locked (in pubspec.lock) to older versions. To update these dependencies, use `flutter pub upgrade`. |
锁文件在应用中的重要性
现在我们可能会问:为什么有一个锁文件这么重要?
答案很简单:
如果我们正在开发一个应用程序,该lock文件保证每个有权访问该项目的人都能够使用我们正在使用的相同版本的库来运行它。它是我们使用的真实版本的“真相来源”。
这意味着即使我们使用^符号,我们也不会在不同机器上有相同依赖项的不同解析版本,这使得与其他开发人员并行开发项目变得更容易。
但是,如果我们查看Dart
官方文档中的What not to commit
,我们会看到不应提交pubspec.lock
文件:
不要将库包的锁文件检查到源代码管理中,因为库应该支持一系列依赖版本。库包的直接依赖项的版本约束应尽可能宽,同时仍确保依赖项将与测试的版本兼容。
结论
了解这个pubspec.lock
文件已经很长时间了,但我们已经成功了
我们现在可以清楚地看到它的重要性——有一种明确的方法来查明我们的应用程序的依赖关系。
此外,我们看到了为什么^
在声明依赖项时应该考虑使用该符号 - 它使我们更加灵活,具有更少的依赖项错误,并尝试我们正在使用的库的更新版本。
但是,在本文中,我们只探讨了使用 pub.dev
中的依赖项。如果我们考虑使用我们自己的git依赖项,我们将不仅知道version
提交哈希,而且知道提交哈希。通过让我们回答一个非常重要的问题,这些信息可以为我们节省无数的调试时间 - 应用程序无法运行是因为我们没有使用每个库的最新版本吗?
在结束之前还有一件事需要考虑——正如Mark O'Sullivan
指出的那样,^在其他技术中使用这种符号会导致Node.js
应用程序中的安全漏洞。您可以在 Chris Laughlin
的演讲中了解更多相关信息:您所有的包都属于我们——保护您的 npm 依赖项。
所以现在您知道如何使用pubspec.lock
文件的力量——它将成为您项目的真实来源,以及一种共享和保持应用程序锁定在相同库版本中的方法。