- 编程语言
- 编译器
- 语言运行时
- 标准库
- 开发工具
【免费下载链接】sdk
The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
本文深入解析 Dart SDK 的语言版本化(Language Versioning)与实验特性开关(Experiment Flags)机制:为什么每个稳定版本都"锁定"一种独立的 Dart 语言、正在开发中的 "in-progress" 版本如何衍生出一族实验语言、库与包如何通过// @dart=注释、SDK 约束与package_config.json精确选中目标语言版本,以及实验特性从"flag 后面"到正式发布的完整流程。读完本文,你将能准确理解--enable-experiment的行为边界,并掌握为包开启实验特性、随发布周期正确调整 SDK 约束的完整实操方案。
核心模型:每一次发布都在"雕刻"一门语言
Dart SDK 对语言版本采取一种非常独特的心智模型,理解它是掌握一切后续机制的前提。该模型在 docs/process/language-versions-and-experiments.md 中被正式阐述,核心原则有三条:
- 每个 major 或 minor(即 ".0")版本的 SDK 发布都会创建一门新的语言版本。一门已发布语言版本的语义,被它在该版本 SDK 中的实际行为彻底固定。
- 任何时刻都存在一个 "in-progress"(进行中)语言版本,它挂着一族彼此相关的语言——每一种对应一组实验特性的不同组合。
- 语言版本化、实验 flag,以及处理核心库和 Flutter 等特殊包所用的"魔法",归根结底都是同一种机制:决定某个库要瞄准众多 Dart 语言中的哪一门。
换言之,--enable-experiment不是"打开一个功能开关",而是"把库切换到另一门语言"。这一视角的转变,是理解 Dart 演进方式的关键。
语言版本:一条被锁定的有序序列
存在一条有序的已发布语言版本序列:2.5、2.6、2.7……每一版都是一门不同的语言。它们之间可能存在不兼容(实践中大体兼容)。每个 Dart 库都瞄准(即"编写于")单一语言版本。一个程序可以包含用多种语言版本编写的库,而一个 Dart SDK 可以同时支持多个不同版本的 Dart 语言。
每当 Dart SDK 发布一个 major 或 minor 稳定版本,对应的语言版本就被"刻进石头"。例如发布 Dart 2.5.0 的那一天起,Dart 2.5 就永远是第一个支持 "constant update"(常量更新)变更的 Dart 版本,不可再改变。文档写作当时,2.5、2.6、2.7 语言版本均已锁定。
Patch 版本(如 2.5.1)不引入新的语言版本。Dart SDK 2.5.0 与 2.5.1 都包含语言版本 2.5。这意味着不能在 patch 版本中发布破坏性语言变更——否则任何已经瞄准该语言版本的用户库都会在升级后瞬间被破坏。
In-progress 版本:尚未定型的那一门
任何时刻还存在一个in-progress 语言版本,它对应当前的 dev 构建,或等价地,对应下一个将要发布的稳定版本。例如文档写作时,由于已发布 2.7.1 而尚未发布 2.8.0,当时的 in-progress 语言版本就是 2.8。
与已锁定的稳定版本不同,in-progress 版本没有"刻进石头"。随着 SDK 的开发,它的行为可能随时变化。
实验语言:in-progress 版本的一族兄弟姐妹
in-progress 版本并非孤身一人,它挂着一族实验语言(experimental languages),每一种对应一组实验 flag 的特定组合。虽然只有一个 Dart 2.7,却可以同时存在多个 Dart 2.8:
- "2.8":bleeding edge 上、未启用任何实验特性时的 Dart。
- "2.8+non-nullable":同样基础上启用了 "non-nullable" 实验。
- "2.8+variance":启用 "variance" 实验。
- "2.8+non-nullable+variance":同时启用两个实验。
所有这些语言同时、并行地存在。Dart 2.6、Dart 2.7、Dart 2.8、Dart 2.8+non-nullable 等在某种意义上"都是真实存在的":它们有(有时并不完整的)规范,有实现它们的工具。请把每一门都看作一门拥有自己名称、语法和语义的独立语言。
因此,不要认为实验 flag 是"开启一个功能"。功能一直在那里,只是位于另一门语言之中。唯一的问题是哪座库想"搬过去"使用它。
可以用如下示意图可视化不同 Dart 风味的空间结构:
┌──────── shipped ─────────┐ ┌─ in-progress ─────────────┐ older... ┄─ 2.5 ─ 2.6 ─ 2.7 ─ 2.8 │ ┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐ 2.8+non-nullable ╎ ╎ │ ╎ no ╎ 2.8+variance ╎ languages ╎ │ ╎ here... ╎ 2.8+triple-shift ╎ ╎ │ ╎ ╎ 2.8+non-nullable+variance └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘ │ ┆ other experiment combinations...图中左侧是向历史深处延伸的、所有已发布稳定版本的数值语言(numeric languages)序列;右侧则是唯一的 in-progress 版本及其旁的各种实验组合。除此之外不存在其他语言——特别地,不存在"已发布版本 + 实验"的组合。你无法在瞄准已发布语言版本的库上"启用某个实验"。一旦某版本发布,它周围的所有实验语言便随之蒸发,并由围绕新 in-progress 版本的全新实验语言取代。今天,"variance" 等特性位于当前 in-progress 版本(仓库中tools/experimental_features.yaml的current-version字段显示为3.14.0)周围,而不是任何已发布的稳定版本周围。
选择一门语言:数值部分如何确定
存在如此之多的 Dart 语言后,"其余一切"都只是让用户选择哪一门的机制。选择的第一步是确定数值部分(如 2.7 还是 2.8),语言版本规范定义了其规则,按优先级排列如下:
- 注释显式选择:
// @dart=2.5这样的注释为库选择对应(数值)版本。在 CFE(Common Front End)实现中,这会通过SourceCompilationUnit.registerExplicitLanguageVersion注册为显式语言版本覆盖,见 pkg/front_end/lib/src/builder/compilation_unit.dart。 - 包内其他库:
package_config.json文件指定它们的语言版本。该文件由 pub 依据用户所用各包 pubspec 中的 SDK 约束生成。 - 无 SDK 约束的包:pub 不会为该包在
package_config.json中写入语言版本,此时库默认使用 "current SDK" 版本。 - 不属于任何包的库:同样默认使用 "current SDK" 版本。
SDK 版本 → 语言版本
"当前 SDK 版本"通常是运行各种 Dart 工具加--version得到的 semver 版本号。将三段式 semver SDK 版本换算为 major.minor 语言版本的规则是:
SDK 的语言版本 = 该 SDK 中工具报告的版本号的 major 和 minor 部分。
由此得出两个推论:
- 稳定版:Dart 2.5.3 的语言版本就是 2.5,符合直觉。
- dev / bleeding edge 版:语言版本是即将到来的稳定版本。例如
dart --version报告 "2.8.0-edge.a38…",其语言版本就是 "2.8"。换言之,非稳定版 SDK 的默认语言版本就是 in-progress 语言。
在仓库内部,SDK 版本号的唯一事实来源是 tools/VERSION,它在发布流程切分支、发版时被更新(当前为 CHANNEL main、MAJOR 3、MINOR 14)。理论上可以从该文件按上述规则推算语言版本,但团队担心语言版本会作为"发布行政事务"的连带后果被无意改变,因此仓库把语言版本显式存放在tools/experimental_features.yaml的current-version字段中。这带来一个理论上的副作用:SDK 报告的版本可能与语言版本失同步;实践中这种偏差应当罕见,且只有自行构建 bleeding edge SDK 的用户才可能察觉。
SDK 约束 → 语言版本
将 SDK 约束换算为语言的规则是:
包使用的默认语言版本 = 其 SDK 约束最低版本(minimum version)的语言版本。
因此以下 SDK 约束分别得到对应的语言版本:
| SDK 约束 | 得到的语言版本 |
|---|---|
>=2.6.0 <3.0.0 | 2.6 |
>=2.6.3 <3.0.0 | 2.6(仍是 2.6) |
>=2.7.1 <3.0.0 | 2.7(仍是 2.7) |
>=2.8.0-dev.1 <3.0.0 | 2.8(in-progress 版本) |
这条规则允许用户瞄准只存在于 dev 版本中的语言版本,也允许用户把 patch 版本作为最低版本,以获得 bug 修复或核心库变更。
实验 Flag:如何登上 in-progress 的"实验支线"
语言版本化解决了"让库落到某个数值版本(含 in-progress 版本 2.8)"的问题。但如果想体验正在开发的实验特性呢?那就需要登上 in-progress 版本的实验兄弟语言——通过向各种工具传入实验 flag(并在analysis_options.yaml中配置)来实现。
这就是实验 flag 的全部作用:向 Dart 工具传入一组实验 flag,意味着把所有使用 in-progress 语言的用户库,当作使用指定实验语言来对待。
两个关键词需要仔细拆解:
- "用户库"(user library):指 Dart 与 Flutter 团队之外的普通用户编写的库(团队自身拥有下文将介绍的特殊权限)。
- "使用 in-progress 语言":该规则只对 in-progress 语言生效。传 "non-nullable" flag 会把所有瞄准 2.8 的用户库挪到 2.8+non-nullable,但对瞄准 2.7 或更早版本的库毫无影响。不存在 2.7+non-nullable 这种东西。2.7.0 发布那天,2.7 被锁定,它周围的实验版本全部蒸发,被围绕新 in-progress 版本 2.8 的一批全新实验语言取代。
发布 SDK 与语言并不等于自动打开当时存在的所有实验 flag。许多被 flag 门控的语言变更会随多个版本漂移,最终才准备好发布。例如 "non-nullable" 和 "variance" 实验在 2.7.0 发布前就存在,之后依然存在。
当新版 SDK 发布时,除非实验特性被刻意"shipped"(行为默认开启、flag 消失),否则 flag 会原样保留为下一个 in-progress 版本中的实验特性。所以发布 Dart 2.7.0 那天,"variance" 不再是影响 Dart 2.7 的 flag,而是变成了影响 Dart 2.8 的 flag。从当前仓库的 tools/experimental_features.yaml 可以看到,non-nullable最终在 2.12.0 正式发布(enabledIn: '2.12.0'),而variance至今仍是一个未默认启用的实验(没有enabledIn字段)。
实验 flag 对所有用户库全局生效
注意一个关键性质:传入实验 flag 会把所有in-progress 版本的用户库都挪到该实验语言上。如果你传了 "non-nullable",你的所有 2.8 库以及你所依赖的每个包中的每个 2.8 库,立刻全部开始瞄准 2.8+non-nullable。
Dart 支持由使用各种已发布版本(如 2.7 和 2.6)的库构成的混合模式(mixed-mode)程序;也可以把它们与一个in-progress 版本(如 2.8 或 2.8+non-nullable)混用。
但 Dart不支持任意组合的实验语言混合。团队不愿定义或实现"一个 2.8+variance 库导入 2.8+non-nullable 库并继承其泛型类"这类场景——"组合的组合"是通往混乱之路。团队内部可能因核心库等原因允许少量混合(见下节),因为可以精心控制处于这种奇怪状态下的代码;但不允许用户编写混合不同实验语言的程序。如果你有一个不希望被正在尝试的实验影响的用户库,请确保该库不在 in-progress 版本上。
SDK 核心库与其他"特权朋友"
实验 flag 是把库从 in-progress 版本挪到其某个实验兄弟版本上的一种方式,但并非唯一方式。再次强调:in-progress 版本的所有实验风味同时存在,实验 flag 主要供用户把自己的库选入这些实验语言。
Dart 团队自身拥有"特殊权力"。已迁移的 SDK 核心库不需要用户传任何实验 flag就能进入 2.8+non-nullable——工具在编译这些特定库时知道自动完成迁移。同样,当 Flutter(以及 vector_math 等少数从其 API 导出的包)迁移时,也可以用白名单或其他特殊手段把它们挪进 2.8+non-nullable。
但所有这些库仍需小心选择正确的数值版本——因为再次强调,不存在 2.7+non-nullable。如果一个核心库没有被标记为 2.8,它就不可能成为 2.8+non-nullable。Dart 的"语言版本化"支持正是库们完成这件事的方式。
在实现层面,这个"特殊权力"体现为sdk/lib/_internal/allowed_experiments.json中的白名单机制:CFE 的isExperimentEnabledInLibrary(见 pkg/front_end/lib/src/api_prototype/experimental_flags.dart)会按库的 canonical URI 区分dart:库与package:包,查表决定是否允许其在未传 flag 时启用实验。当前仓库的 allowed_experiments.json 即定义了sdkExperiments与macros两个实验集。
实操:如何让库用上实验特性(以 null safety 为例)
把上述机制串起来,看看当年要让自己库用上?和late需要做哪几步(null safety 现已随 2.12.0 正式发布,但该流程对当前仍在 flag 之后的任何实验特性——如仓库中的macros、variance、data-assets等——完全适用):
使用 dev 或 bleeding edge 构建的 SDK。当时的稳定版最高只支持语言 2.7,不支持 null safety。
告诉 Dart 你的库应被当作 2.8 处理。核心库用的是版本注释和/或某种硬编码;包则通过 pubspec 的 SDK 约束:
environment: sdk: '>=2.8.0-dev.0 <2.8.0'- SDK 最低约束:可以按需提高 dev 版本要求,关键是最低版本至少是2.8.0 的一个 dev 版本。也可以完全省略 SDK 约束——这对应用包可行,但对库包不行,因为 pub 不允许发布没有 SDK 约束的包。
- SDK 最高约束:相对较低的上限(
<2.8.0)为 2.8.0 之前"搞破坏"留出了回旋余地。严格来说并非必须——写成<3.0.0时本文所述的一切依然成立,但当时并不明智地宣称现在发布的包能一路兼容到 3.0.0。
让编译器把所有 2.8 用户库挪到 2.8+non-nullable,即在调用工具时传
--enable-experiment=non-nullable,并在analysis_options.yaml中做类似配置以获得 IDE 支持。完整细节见 docs/process/experimental-flags.md。
完成这三步,Dart 工具就知道该库瞄准的是 2.8+non-nullable(至少在当下如此)。
实验 Flag 的 CLI 与 IDE 使用规范
上述第 3 步涉及的 flag 用法,在 docs/process/experimental-flags.md 中有明确的工程规范。之所以需要这套规范,是因为 Dart SDK 通过多条渠道发布(独立 SDK、Flutter SDK、Google 内部渠道),各渠道发布日历不同,任何 dev channel 构建都有可能在某个渠道成为正式发布的 SDK,因此必须保证 dev channel 质量与"哪些功能完整可用"的一致性,同时又要允许不完整功能落地,以支持跨组件特性组合、自动化测试、合作方与客户的预览等需求。
结论是:所有进行中的特性都必须放在同一组 flag 之后;对 flag 背后特性的变更不视为破坏性变更(即使该特性曾出现在稳定 SDK 中),且随时可能被移除。关于破坏性变更的完整流程见 docs/process/breaking-changes.md。所有破坏性变更与所有语言变更必须(在可能时)放在 flag 之后开发;较大、将跨越数周甚至多个版本处于中间状态的用户可见变更,以及可能带来显著负面性能影响的变更,也建议如此。
CLI 工具的 flag 格式
Flag 由一个或多个小写单词用连字符连接组成。其唯一事实来源是一个共享的 .dart 文件,各工具提供查询框架以便轻松访问新 flag。flag 通过--enable-experiment传给 CLI 工具,可传多个 flag(逗号分隔或重复传参):
dart --enable-experiment=super-mixins dart --enable-experiment=super-mixins,no-slow-checks,preview-dart3 dart --enable-experiment=super-mixins --enable-experiment no-slow-checks --enable-experiment preview-dart3若用户传入无法识别的 flag(例如 flag 已不再受支持),工具必须打印到 stderr 提示、但不失败:
dart --enable-experiment better-mixins Unknown experiment flag 'better-mixins'.在 CFE 实现中,--enable-experiment被定义为共享的 Option(见 pkg/front_end/lib/src/base/command_line_options.dart),生成的ExperimentalFlag常量类(见 pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart)为每个 flag 携带name、isEnabledByDefault、isExpired、experimentEnabledVersion、experimentReleasedVersion五个属性。
UI 工具(IDE / 编辑器等)的 flag 格式
能调用 Dart 工具的 IDE 与编辑器必须支持传递这些 flag,且支持方式应通用灵活,增删 flag 时无需改动 UI。通常有两种形式:
影响分析的实验,在
analysis_options.yaml的单个enable-experiment:键下启用,例如启用super-mixins与no-slow-checks:analyzer: enable-experiment: - super-mixins - no-slow-checks影响启动/运行行为的实验,在 IDE 特定的运行配置(Run Configuration)中,传入与 CLI 相同的
--enable-experimentflag。
当前全部实验 flag 的定义集中在 tools/experimental_features.yaml,各工具据此访问。
从源码看实验 Flag 的生命周期
tools/experimental_features.yaml是实验特性的唯一事实来源,其features映射中的每个条目包含以下字段:
| 字段 | 必填 | 含义 |
|---|---|---|
help | 是 | 实验特性的人类可读描述 |
enabledIn | 否 | 特性正式发布的 SDK 版本(<major>.<minor>)。指定后该特性无视 SDK 实际版本默认启用;省略则默认禁用,但可通过--enable-experiment=<flag>启用。低于该版本的语言版本(如// @dart=2.12或 package config)可关闭该特性 |
experimentalReleaseVersion | 否 | 可通过 flag 启用该实验的 SDK 版本(<major>.<minor>)。若sdk/lib/_internal/allowed_experiments.json被更新,此字段指定对这些库与包启用实验的版本 |
expired | 否 | 为 true 时 flag 无法再通过命令行启用,条目待从文件移除;省略视为 false |
validation | 否 | 一个打印 "feature enabled" 的程序(stdout),特性启用时编译通过、否则抛错;用于对每个实验运行通用测试 |
category | 否 | 指定实验影响的组件(如CFE、vm、language),默认language(同时为 CFE 与 Analyzer 生成代码) |
实验通过若干状态演进(详见文件头注释 tools/experimental_features.yaml):
- Disabled(禁用):刚加入时省略
enabledIn与expired,默认禁用但可通过 flag 启用,实现团队在 flag 后构建功能,用户可先行尝试。 - Experimental release(实验发布):想让特定库与包默认启用时,向
sdk/lib/_internal/allowed_experiments.json白名单添加条目并提升experimentalReleaseVersion;其他库与包仍需显式传 flag。 - Shipped(已发布):添加
enabledIn指明包含该特性的 SDK 版本,此时传 flag 无效(默认启用且无法关闭)。 - Retired / Rejected(退役/否决):添加
expired。若特性已发布则 flag 退役,若未发布则整个实验被否决;用户传该 flag 会收到警告但工具继续运行。
另外,current-version字段指定正在开发的 Dart 版本,未声明自身版本的 Dart 源文件被默认视为该版本,实验 flag 不影响声明了更早版本的文件。
该 YAML 通过代码生成器驱动整个工具链:tools/generate_experimental_flags.dart为 VM 生成 runtime/vm/experimental_features.h 与 runtime/vm/experimental_features.cc;CFE 侧则由dart pkg/front_end/tool/cfe.dart generate-experimental-flags生成 experimental_flags_generated.dart。生成代码中,每个 flag 的experimentEnabledVersion在未指定enabledIn时即为defaultLanguageVersion(当前 in-progress 版本),从数据上印证了"实验语言只存在于 in-progress 版本周围"这一核心模型。
在 CFE 的查询框架中,experimental_flags.dart 提供全局态(GlobalFeatures)与库级态(LibraryFeatures)两层接口:全局态由explicitExperimentalFlags(命令行传入)与 flag 默认值共同决定;库级态再叠加 canonical URI(dart:/package:)对应的 allowed 白名单,这正是"核心库不传 flag 也能进入实验语言"的实现细节。语言版本本身则由 source_library_builder.dart 中的LanguageVersion/ImplicitLanguageVersion类表达——前者来自显式声明,后者来自包的默认版本。
发布推演:实验特性随稳定版发布的三种命运
理解上述机制后,用两个假想场景演示"语言版本锁定"对生态的连锁影响。文档以 null safety 为例,但结论对任何实验特性通用。
场景一:2.8.0 发布但不包含 null safety
假设发布 Dart 2.8.0 稳定版而 null safety 仍处于 "non-nullable" flag 之后。发布当天,2.8 不再是 in-progress 版本,被刻进石头,其语言版本严格对应发布 SDK 的行为;所有 2.8 实验版本消失,flag 不再影响使用语言 2.8 的库;与此同时,新的 in-progress 版本 2.9 出现,所有未发布而结转的 flag 转而作用于它——于是出现 2.9+non-nullable、2.9+variance 等。
2.8 关闭、2.9 开启意味着三件事:
任何瞄准 2.8 并使用 null safety 特性的库,都必须把语言版本改为瞄准 2.9。这可能意味着修改
// @dart=2.8注释,或在 pubspec 中抬高最低 SDK 约束。刚发布的 2.8.0 稳定 SDK 中,核心库的语言版本必须是 2.9。因为核心库使用了 null safety,而该特性已无法对 2.8 库启用。这看起来很怪——2.8.0 如何能支持一个未来的 Dart 版本?现实是 2.8.0 对 2.9 的某个子集拥有秘密的内部支持,核心库恰好落在其中。这是实现细节:核心库使用了 SDK 内尚未对外暴露的能力。虽显奇怪,但应该可以接受。
2.8.0 发布后,Flutter 用户玩 null safety 所需的所有包,其最低 SDK 约束必须高于 2.8.0。它们需要到达语言级别 2.9,唯一途径是排除 2.8.0 的约束。但如果用户跑在 Dart 2.8.0 稳定版上,Pub 不会选中这些包——因为 2.8.0 落在其 SDK 约束之外!
这是一个真实的问题,根源在于用 SDK 约束同时控制语言版本与包解析。为此,稳定版发布后不久,团队会再发布一个 Dart 2.9.0-dev.0 dev 版本并合入 Flutter 的 dev channel。想体验 non-nullability 的用户必须待在 dev channel——null safety 仍是实验特性,而稳定版的意义就在于稳定;想实验实验特性,就去 dev channel。
场景二:2.10.0 正式发布 null safety
接着假设 null safety 最终在 2.10.0 正式发布。所有在玩 null safety 实验的包早已把最低 SDK 约束抬到类似>=2.10.0-dev.0。此时各包命运分三种:
- 若 SDK 约束形如
>=2.10.0-dev.0 <2.10.0:只需把上限抬到包含 2.10.0。这是安全的选择——假设下一个稳定版本与先前的 in-progress dev 构建兼容是有风险的,dev 构建的意义就在于"在变动中"。因此多数使用 null safety 的包应处于此状态。发布 null safety 后,验证包在稳定版下依然正常,然后抬高上限即可。 - 若 SDK 约束形如
>=2.10.0-dev.0 <3.0.0:作者无需任何操作。该约束声称同时支持先前 dev 版 null safety 与已发布的稳定版。这种宽约束是可疑的,因为团队保留对 flag 门控特性做任意破坏性变更的权利;但如果作者确信没有(也不会)发生破坏(典型情况是"作者"本身就是 Dart 团队成员),宽约束可以合理。此时包继续工作——它已经在语言 2.10 上,而 2.10 现在原生支持 null safety,无需改动。 - 若 SDK 约束形如
>=2.8.0 <3.0.0:包停留在之前的 "legacy" 语言版本,相当于"退出了 NNBD",像以前一样继续工作。
场景中的 "2.10.0" 并无特殊之处:无论何时、以何种版本发布某个实验特性,围绕包的影响都应遵循上述规律。
结语:一个机制,三种视角
回顾全文,Dart 的语言演进可以被概括为一句话:已发布版本把语言钉死,in-progress 版本不断衍生实验语言,而语言版本化、实验 flag 与白名单全部只是"库选择语言"的机制。对 SDK 用户与包作者而言,这意味着:向稳定版发布破坏性语言变更永远不可能(patch 版不引入新语言版本);在 in-progress 版本上,实验 flag 会全局性地影响所有瞄准该版本的库(包括传递依赖);一旦稳定版发布,实验就随之迁移到下一个 in-progress 版本;而 SDK 约束既是包解析的门槛,也是包的语言版本声明——理解这双重身份,才能安全地为包选择"何时升高、何时收窄"约束。本文涉及的全部配置源文件与实现源码,均可在仓库的 tools/experimental_features.yaml、docs/process/experimental-flags.md 与 pkg/front_end/lib/src/api_prototype/ 中进一步查阅。
- 编程语言
- 编译器
- 语言运行时
- 标准库
- 开发工具
【免费下载链接】sdk
The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
相关推荐
Dart语言版本管理与实验性功能机制详解
Dart语言版本管理与实验性功能机制详解 前言 Dart作为一门现代化的编程语言,其语言特性的演进和版本管理机制对于开发者来说至关重要。本文将深入解析Dart语
编程语言编译器语言运行时标准库开发工具Open WebUI 本地部署完整指南:3 种方式一次装好,对话数据全留本机
Open WebUI 本地部署完整指南:3 种方式一次装好,对话数据全留本机 Open WebUI 本地部署后是一个完全离线的自托管 AI 对话界面:它负责对话
人工智能大模型AI 应用RAGAI Agent本地部署交互助手后端前端Keyviz 版本控制策略:语义化版本与发布流程解析
Keyviz 版本控制策略:语义化版本与发布流程解析 引言:版本混乱的痛点与解决方案 在开源项目开发中,版本号的混乱往往导致用户困惑、开发者协作困难以及部署风险
桌面应用交互助手
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考