Flutter导入:使用相对路径还是包名?

109

在Flutter中,如果要导入位于我们自己包的lib目录下的库,应该使用相对导入吗?

import 'foo.dart'

导入模块,还是导入包?

import 'package:my_app/lib/src/foo.dart'

Dart规范建议使用相对路径来导入位于您自己包的lib目录内的库:

当导入位于您自己包的lib目录内的库时,请首选相对路径。

Provider包则指出,始终使用packages导入

  • 始终使用packages导入。例如:import 'package:my_app/my_code.dart';

除了简洁性外,这两种方法有什么区别?为什么使用packages导入可以减少错误?

7个回答

68

根据Dart指南中的同样内容,他们进一步解释了相对导入的原因:

没有什么深刻的理由去更喜欢前者,只是它更短而已,我们希望保持一致。

个人而言,尽管绝对路径更冗长,但我更喜欢使用绝对路径,这意味着当我从不同文件夹中的不同dart文件中导入时,我不必计算出要导入的文件与当前文件的相对位置。以下是一个虚构的例子:

我有两个位于不同文件夹级别的dart文件需要导入themes/style.dart:

其中一个是widgets/animation/box_anim.dart,相对路径导入将如下所示:

import '../../themes/style.dart';

另一个是相对导入的 screens/home_screen.dart

import '../themes/style.dart';

这可能会让人感到混乱,因此我发现最好在两个文件中都使用绝对路径,保持一致:

import 'package:myapp/themes/style.dart';

保持这个规则的一致性非常重要。因此,无论你使用什么方法 - 一致性是关键!

Dart语言代码检查工具(Linter for Dart)包也谈到了避免在/lib文件夹中使用相对导入,但更多地关注于在'/lib'文件夹中混合使用时不应该做的事情:

避免在lib/中使用相对导入。

当混合使用相对和绝对导入时,可能会出现同一个成员以两种不同的方式导入的混淆情况。避免这种情况的简单方法是确保您没有在其路径中包括lib/的相对导入。


45

简述; 选择你喜欢的,注意prefer_relative_imports 在官方Effective Dart guide中推荐使用。

首先,如this answer所提到的,Provider不再推荐包导入。

Dart linter 提供了一系列规则,包括一些预定义的规则集:

导入规则

实际上有超过两个相反的规则关于导入:


以下是您提到的两个规则:
- prefer_relative_imports,在没有预定义规则集中启用,但在Effective Dart guide中推荐使用,与以下规则相反: - always_use_package_imports,在没有预定义规则集中启用。这意味着你可以根据自己的喜好启用它(请注意,它与前面的规则不兼容)
应该选择哪一个?
选择你想要的规则!它不会导致任何性能问题,并且没有规则会减少错误。只需选择一个并通过Dart linter使你的导入在整个项目中保持一致。
我个人更喜欢使用prefer_relative_imports,因为它是Dart团队推荐的,配合this VSCode extension,可以自动修复和排序我的导入。

1
不错的选择,特别是使用VSCode扩展,可以节省很多时间。 - serg
vscode-dart-import 扩展正常工作。谢谢。 - mahfuz
如果你使用build_runner,那么你绝对应该使用相对导入(https://github.com/dart-lang/build/issues/3526#issuecomment-1576934849)。 - Flawn
如果你使用 build_runner,那么你肯定应该使用相对导入(https://github.com/dart-lang/build/issues/3526#issuecomment-1576934849)。 - undefined

14

现在提供程序不再需要导入包。

这是对旧Dart错误的一种解决方法:Flutter:从子级检索顶级状态返回null

简而言之,通过混合相对和绝对导入,有时Dart会创建一个类定义的副本。

这导致了这个荒谬的行:

import 'package:myApp/test.dart' as absolute;
import './test.dart' as relative;

void main() {
  print(relative.Test().runtimeType == absolute.Test().runtimeType); // false
}

由于provider依赖于runtimeType来解析对象,因此这个错误使得在某些情况下provider无法获取对象。


2
你用的是过去式,这个 bug 现在已经解决了吗? - Augustin R
31
但是,何时使用哪种导入方式的问题仍然存在。是否有规则或最佳实践,可以确定何时使用其中一种方法以及何时使用另一种方法? - Stacky

6

我对这个话题的看法是,绝对(package:my_app/etc/etc2...)导入比相对导入(../../etc/etc2...)更不容易出问题,因为当您决定重新组织/整理项目结构时,每当您将文件从一个目录移动到另一个目录时,都会更改此文件使用的每个相对导入的“起始点”,从而破坏移动文件内部的所有相对导入...

基于这个原因,我个人总是更喜欢使用绝对路径而不是相对路径。


我曾经看到后者是真的,特别是在IntelliJ中重构时,使用绝对导入会出现问题,这可能也是IDE/工具问题。 - humblerookie

3
这个问题已经有很好的答案了,但我想提及一个非常烦人且难以找到的问题,这个问题与单元测试有关,它是由相对导入引起的。
异常捕获 expect 块的 expect 失败指示器。
expect(
  () => myFunction,
  throwsA(isA<InvalidUserDataException>())
);

在显示实际结果与期望结果完全相同且没有任何失败原因的情况下,经过大量试错,发现问题是由于测试文件中导入期望的InvalidUserDataException(一个自定义类)时使用了相对路径而不是包路径。

为了找到这个问题,我必须逐行、逐个调用地与另一个使用完全相同异常期望者的测试文件进行对比(幸运的是我们有这个),并偶然间滚动到此文件的导入顶部,看到蓝色的下划线显示prefer relative imports to /lib directory

不,它们并不被推荐;它们是必需的,因为一旦我将其更改为包(绝对)导入,一切都突然开始工作了。

我从中学到的是:对于测试文件(位于lib目录之外的文件),请使用绝对导入

  • 例如,在src/test/main_test.dart内部
    • 不要:使用import '../lib/main.dart'
    • 请使用package:my_flutter_app/main.dart

也许其他人已经知道这一点,但我不知道,而且我在搜索这个问题时找不到任何在线资源,所以我想分享我的经验,希望能帮助其他陷入困境的人。

有人知道为什么会发生这种情况吗?

编辑:为了背景,这是在使用Flutter 2.1.4(稳定版)和Sound Null Safety时发生的。


1
我不确定是什么原因导致了你的问题,但是你的发现在这里提到了,看看这些文档可能值得一试,以避免未来的麻烦 :). https://dart.dev/guides/language/effective-dart/usage 特别是“不要允许导入路径进入或退出lib。”部分: “(...) lib内部文件中的相对导入路径无法访问lib目录外的文件,而lib外的库也无法使用相对路径进入lib目录。任何一种情况都会导致混乱的错误和程序崩溃。” - Kasia K.
@KasiaK。我看了你的参考资料,它确实说不要那样做,但也没有说明为什么会发生这种情况,并且在我的VS Code上似乎没有对其进行代码检查。无论如何,感谢提供链接。 - daddy7860
我不明白这个回答与页面顶部的问题有何关联,但它应该有。请根据[答案]进行[编辑]或删除回答。否则,它可能会被标记为“不是答案”并被删除。 - Yunnosch

1

你使用集成测试吗?

如果答案是肯定的,在大多数情况下,你需要使用包导入。当你尝试在物理设备上运行你的集成测试时,任何相对导入都无法找到它们正在寻找的内容。

例如:https://github.com/fluttercommunity/get_it/issues/76

你可以通过使用这两个linting规则来强制在你的项目中使用包导入:

我也喜欢使用package imports,因为即使在重新排列文件和文件夹时,它们也会保持不变。相对导入经常会出问题,而且删除它们并重新导入有问题的依赖项很麻烦。


0

不使用包导入的一个非常简单的原因:重命名您的包而无需编辑每个Dart文件

在产品没有确定名称时,可能会多次更改名称,您或您的产品所有者决定更改名称。

如果使用包导入,重命名将变得更加痛苦,因为您的包名称出现在每个导入中。

当然,您可以使用查找/替换查询进行更改,但这是每个Dart文件上的无用编辑,您可以通过相对导入避免这种情况。

此外,VSCode允许在文件移动/重命名时自动更新相对导入,我从未遇到过任何问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接