00 认识 Dart

在写第一行 Dart 代码之前,先回答三个问题:Dart 是什么、它为什么存在、它和 C 有什么根本不同。本章不要求你安装任何工具,目标是在脑中建立一张地图——知道这门语言从哪里来、适合做什么、接下来该怎么学。地图对了,后面的每一章都会落在正确的位置上。


一、Dart 是什么

1.1 一句话定义

Dart 是 Google 于 2011 年发布的一门面向对象、静态类型、兼具 JIT 与 AOT 两种运行模式的编程语言。用一句话概括它的现状:

Dart 是为 UI 而生的语言,也是 Flutter 框架唯一的官方开发语言。

这句话不是营销口号,而是理解 Dart 全部设计取舍的钥匙:Dart 的每一个语法特性,几乎都能追溯到「让开发者更快地写出跨平台界面」这个目标。

1.2 关键事实速览

项目内容
诞生2011 年,Google 发布
1.0 版本2013 年 11 月
当前主线Dart 3.x(本教程基于 Dart 3.x)
类型系统静态类型 + 健全空安全(sound null safety)
内存管理分代 GC,没有 free,也不存在悬垂指针
运行模式JIT(开发期)/ AOT(发布期)/ JavaScript / WebAssembly
包管理pub,配置文件 pubspec.yaml,仓库 pub.dev
主要用途Flutter 跨平台 UI、命令行工具、服务端、云函数
官方站点dart.dev

1.3 Dart 与 Flutter 的关系

初学者最容易混淆的一对概念。用一张图说清:

flowchart TD
    A["Dart 语言<br/>语法、类型系统、标准库"] --> B["Flutter 框架<br/>Widget、渲染、动画、路由"]
    B --> C["Android 应用"]
    B --> D["iOS 应用"]
    B --> E["Web 应用"]
    B --> F["Windows / macOS / Linux 桌面应用"]
    A --> G["服务端 / CLI / 脚本"]
    A --> H["Flame 游戏引擎"]
  • Dart 是语言,Flutter 是用 Dart 写的 UI 框架
  • 学 Flutter 之前必须先掌握 Dart 语法与异步模型,否则会在 FutureStream、状态管理面前寸步难行
  • 反过来,不学 Flutter 也能用 Dart 写 CLI 工具和服务端程序——本教程前两个阶段完全不依赖 Flutter

二、发展史时间线

理解一门语言的「黑历史」与转折点,能解释它今天为什么长这样:

timeline
    title Dart 发展时间线
    2011 : Google 在 GOTO 大会首次公开 Dart
    2013 : Dart 1.0 发布,自带可选类型与 Dartium 浏览器
    2015 : Flutter 前身 Sky 项目启动
    2017 : Flutter 首个 alpha 版发布
    2018 : Dart 2.0 转向健全强类型,Flutter 1.0 发布
    2021 : Dart 2.12 引入健全空安全,Flutter 2.0 支持全平台
    2023 : Dart 3.0 发布 records、patterns、class modifiers
    2024 : dart compile wasm 走向稳定,Flutter 全面换用 Impeller 渲染引擎

几个转折点值得记住:

年份事件意义
2011Dart 首次公开目标是替代 JavaScript 写大型 Web 应用,早期反响平平
2013Dart 1.0定位仍是 Web 语言,可选类型是妥协产物
2017Flutter alphaDart 找到了真正的杀手级应用场景:移动 UI
2018Dart 2.0抛弃可选类型,改为健全静态类型,性能与工具链大幅提升
2021空安全StringString? 成为两种类型,空指针在编译期被拦截
2023Dart 3.0模式匹配、记录、类修饰符,语言表达力追平现代语言
2024+Wasm除 JS 之外的第二条 Web 编译路径,性能接近原生

一句话总结这段历史:Dart 没有靠「更好的 JavaScript」成功,而是靠「Flutter 的语言」成功


三、设计目标:为 UI 优化的语言

Dart 的三大设计目标与对应机制:

设计目标实现机制开发者得到的收益
迭代要快JIT + 热重载(Hot Reload)改一行 UI 代码,毫秒级看到效果,应用状态不丢失
发布要快AOT 编译为原生机器码启动快、运行时无解释器开销,性能接近 C++
写 UI 要顺声明式、响应式语法Widget 是普通对象,UI 即状态的函数,无需手动操作控件
跨平台要一致自带渲染引擎(Skia/Impeller)同一套代码在各平台像素级一致,不依赖平台原生控件
大型项目要稳静态类型 + 空安全 + 静态分析大量错误在 dart analyze 阶段暴露,而不是上线后

其中「热重载」是 Flutter 开发体验的核心竞争力。与 C 的编译流程对比:

环节CDart + Flutter
修改代码后make 重新编译链接保存文件,热重载自动注入
等待时间秒级到分钟级通常小于 1 秒
应用状态进程重启,状态清零保持当前页面与数据,只替换代码
底层原理JIT 保留类型信息,增量编译并替换函数入口

四、JIT 与 AOT:两种运行模式

4.1 概念对照

  • JIT(Just-In-Time,即时编译):程序运行时把热点代码编译成机器码,可以边跑边优化。开发期用它,因为有类型信息和运行时反馈,编译快、支持热重载
  • AOT(Ahead-Of-Time,提前编译):构建时一次性编译成目标平台的原生机器码,运行时不带编译器。发布期用它,启动快、体积可控

4.2 与 C 对比

维度CDart JITDart AOT
编译时机全部提前(本质是 AOT)运行时增量编译构建时全量编译
开发迭代每次改代码都要重新编译链接热重载,毫秒级不用于开发
运行性能中高,热点可优化高,接近 C
启动速度需要加载 VM 与内核快照
典型命令gcc main.c -o maindart run main.dartdart compile exe main.dart
调试体验gdb完整调试、热重载可调试但无热重载

4.3 编译流程全景

flowchart TB
    SRC["main.dart 源码"] --> CFE["前端编译器 CFE<br/>词法/语法/类型检查"]
    CFE --> KERNEL["Kernel 中间表示<br/>.dill 文件"]
    KERNEL --> JIT["JIT 运行时<br/>dart run"]
    KERNEL --> AOT["AOT 编译器<br/>dart compile exe"]
    KERNEL --> JS["dart2js<br/>dart compile js"]
    KERNEL --> WASM["dart compile wasm"]
    JIT --> DEV["开发期: 热重载 / 快速启动"]
    AOT --> PROD["发布期: 原生可执行文件"]
    JS --> WEB["浏览器: JavaScript 产物"]
    WASM --> WEB2["浏览器: WebAssembly 产物"]
    style KERNEL fill:#e8f0fe,stroke:#4285f4

关键点:无论 JIT、AOT 还是 Web,Dart 都先编译到统一的 Kernel 中间表示。这就是为什么同一条 dart analyze 能在所有目标上给出相同诊断,也是 Flutter 热重载能做到毫秒级的架构基础。

4.4 两种模式的实际命令

# 开发:JIT 运行,支持热重载(Flutter 场景)
dart run bin/main.dart
 
# 发布:AOT 编译为原生可执行文件,无运行时依赖
dart compile exe bin/main.dart -o myapp
./myapp
 
# 发布:编译为 JavaScript(Web 场景)
dart compile js bin/main.dart -o main.js

五、Dart 能做什么

领域代表技术入门方式与 C 的关系
移动应用Flutterflutter create完全不同的技术栈,但性能目标一致
桌面应用Flutter Desktopflutter create --platforms=windows,macos,linux对应 Qt/GTK 的位置
Web 应用Flutter Web、Jasprdart compile js / dart compile wasm对应 Emscripten 的位置
服务端shelf、dart_frogdart create -t server-shelf对应 nginx 模块 / CGI 的位置
命令行工具dart:io、args 包dart create -t console直接对标 C 的 CLI 程序
云函数Google Cloud Functions 等编译为 exe 或部署源码免运维的部署形态
游戏Flame 引擎flutter pub add flame对标 SDL 的位置
嵌入式/无屏设备Dart 嵌入式运行时定制编译对标精简版 libc 程序
跨语言调用dart:ffiDynamicLibrary.open反过来调用已有的 C 库

结论:Dart 不是「Flutter 专用语言」,而是「UI 优先的通用语言」。本教程入门与深入阶段全部使用命令行程序,Flutter 从第三阶段才开始。


六、Dart VM 运行时架构概览

先建立粗粒度认知,具体机制在「2 深入」阶段展开:

flowchart LR
    subgraph APP["你的程序"]
        A1["dart:core<br/>dart:io<br/>第三方包"]
    end
    subgraph VM["Dart VM"]
        B1["运行时核心<br/>对象模型 / 类加载"]
        B2["GC<br/>分代垃圾回收"]
        B3["JIT 编译器"]
        B4["AOT 编译器"]
        B5["Isolate 调度<br/>独立堆 + 消息端口"]
    end
    subgraph NATIVE["原生层"]
        C1["操作系统 API"]
        C2["C/C++ 库 (FFI)"]
    end
    APP --> VM
    VM --> NATIVE
组件职责与 C 的对应物
运行时核心对象模型、类型检查、类加载C 运行时启动代码 crt0
GC自动回收不可达对象无,C 靠 free 手动管理
JIT 编译器运行时编译与优化
AOT 编译器生成原生机器码gcc/clang 的角色
Isolate并发执行单元,堆内存相互隔离类似多进程,不是 pthread
FFI调用 C 函数、操作原生内存反向的「C 调 Dart」

七、与 C/Java/TypeScript/Kotlin 对比

维度CDartJavaTypeScriptKotlin
类型系统静态、弱类型检查静态、健全空安全静态、可空标注静态、结构化类型静态、健全空安全
内存管理手动 malloc/freeGCGC + JVMGC + JS 引擎GC + JVM/原生
编译目标原生机器码原生/JIT/JS/Wasm字节码JavaScript字节码/原生
启动速度极快AOT 快JVM 较慢依赖 JS 引擎JVM 较慢
并发模型pthread 共享内存Isolate 消息传递线程共享内存单线程事件循环协程 + 线程
空指针风险运行时崩溃编译期拦截运行时 NPEstrict 模式下编译期编译期拦截
字符串char* + 终止符UTF-16 不可变 StringUTF-16 StringUTF-16 stringUTF-16 String
包管理无官方方案pubMaven/GradlenpmGradle
主要场景系统/嵌入式跨平台 UI/CLI企业后端/安卓Web 前后端安卓/后端

选型建议:

  • 已有 C 代码资产、要对接硬件或追求极致性能:继续用 C
  • 要一套代码覆盖移动 + 桌面 + Web 的界面:Dart + Flutter
  • 企业 Java 生态、后端为主:Java/Kotlin
  • 纯 Web 前端且团队是 JS 背景:TypeScript

八、与 C 的哲学差异

8.1 内存:malloc/free 与 GC

/* C: 谁申请谁释放,忘记 free 就泄漏,free 两次就崩溃 */
int *p = malloc(sizeof(int) * 100);
if (p == NULL) return -1;
p[0] = 42;
free(p);
p = NULL;
// Dart: 只管 new,回收交给 GC,不存在 free 与悬垂指针
void main() {
  final list = List<int>.filled(100, 0);
  list[0] = 42;
  print(list[0]);   // 42
  // 离开作用域后,list 不可达时由 GC 自动回收
}
维度CDart
申请mallocList()Map()、构造函数
释放free(必须,且只能一次)GC 自动,无需干预
悬垂指针可能,未定义行为不可能,GC 保证对象存活
内存泄漏忘记 free 必然泄漏只可能因「逻辑上仍被引用」而泄漏
代价无运行时开销有 GC 暂停与额外内存开销

8.2 空值:NULL 与可空类型

/* C: 编译器不检查 NULL,运行时才崩溃 */
char *name = get_name();
printf("%zu\n", strlen(name));  /* name 为 NULL 时直接段错误 */
// Dart: 可空性写进类型,编译器强制你处理
String? getName() => null;
 
void main() {
  final name = getName();
  // print(name.length);      // 编译错误:name 可能为 null
  print(name?.length ?? 0);   // 正确:空感知访问 + 默认值,输出 0
}
维度CDart
空值表示NULL 宏,本质是 0null 关键字
类型标记无,任何指针都能为 NULLString? 显式标记可空
检查时机运行时(崩溃时)编译期(分析器直接报错)
强制处理必须 ?.?? 或判空后才能用

8.3 其他哲学差异总表

主题C 的哲学Dart 的哲学
错误处理返回错误码,调用者自觉检查异常机制,未捕获就崩溃并给栈
类型转换强制转换即信任程序员as 转换失败会抛异常,is 先检查
代码组织头文件 + 源文件 + 链接库 + import,无头文件
并发线程共享内存,锁保护Isolate 隔离内存,消息通信
预处理器文本替换无宏,用 const 与代码生成替代

九、本教程学习路线图

flowchart TD
    P0["Phase 0 环境准备<br/>00 认识 Dart / 01 环境配置"] --> P1["Phase 1 语言入门<br/>02 第一个程序 / 03 变量与类型<br/>04 运算符与流程 / 05 函数与闭包"]
    P1 --> P2["Phase 1 后半<br/>06 集合 / 07 面向对象 / 08 空安全<br/>09 异步 / 10 异常 / 11 包管理"]
    P2 --> P3["Phase 2 语言深入<br/>泛型 / Isolate / FFI / 元编程 / 测试 / 编译产物"]
    P3 --> P4["Phase 3 Flutter 框架<br/>布局 / 状态 / 路由 / 网络 / 动画 / 发布"]
    P4 --> P5["Phase 4 工程化<br/>架构 / CI/CD / 包发布"]
    style P1 fill:#e8f0fe,stroke:#4285f4

你当前的位置:Phase 0 第 0 章。接下来依次是:

序号章节内容
101 环境配置装 SDK、配镜像、跑通 flutter doctor
202 第一个程序与命令行mainprintdart run、项目结构
303 变量与类型var/final/const/late、数值、字符串
404 运算符与流程控制运算符、分支、循环、模式匹配
505 函数与闭包参数、箭头函数、闭包、高阶函数

十、如何学习本教程

  1. 对照式阅读:每章都配「与 C 对比」表格。读的时候主动问自己两个问题——这个概念在 C 里对应什么?Dart 为什么改掉它?
  2. 代码必须亲手敲dart analyze 会在你写错时立刻指出问题,报错信息本身就是最好的教材。把示例改坏、读报错、再修好,比复制粘贴有效十倍。
  3. 用 DartPad 当草稿纸dartpad.dev 是在线 Playground,任何「如果这样写会怎样」的疑问,先去那里花三十秒验证,不要靠猜。
  4. 练习不要跳过:每章末尾一道题,难度不高但覆盖本章知识点。做完再进入下一章。
  5. 建立差异清单:准备一个持续追加的笔记文件,每学完一章往里加一行 C 与 Dart 的差异,期末回看会非常有价值。
  6. 不要提前碰 Flutter:语法地基不牢时学 Flutter,会在状态管理与异步面前反复受挫。前两阶段老老实实写命令行程序。

常见坑:认知层面的误区

误区事实
「Dart 只能写 Flutter」CLI、服务端、游戏、嵌入式都能写,dart compile exe 产物无运行时依赖
「Dart 是解释型语言,所以很慢」AOT 产物是原生机器码,性能与 Java/Kotlin 同级,部分场景接近 C++
「Dart 和 JavaScript 差不多」语法有相似处,但 Dart 是健全静态类型 + 空安全,心智模型更接近 Java/Kotlin
「学了 C 就不用学 Dart 的类型系统」Dart 的空安全与 C 的 NULL 检查是两套完全不同的机制,必须重新建立直觉
「GC 语言不用管内存」不需要 free,但仍要避免长生命周期引用导致的逻辑泄漏
「热重载等于热重启」热重载保留状态只换代码;热重启会重置状态,二者适用场景不同

本章小结

  • Dart 是 Google 2011 年发布、2013 年发布 1.0 的静态类型语言,是 Flutter 的官方语言
  • 发展史的关键转折:2018 年强类型化、2021 年空安全、2023 年 Dart 3 模式匹配、2024 年 Wasm
  • 三大设计目标:开发快(JIT 热重载)、发布快(AOT 原生码)、写 UI 顺(声明式响应式)
  • 所有目标平台共享 Kernel 中间表示,这是统一工具链的架构基础
  • 与 C 的两大哲学差异:GC 替代手动内存管理;可空类型替代运行时的 NULL 检查
  • 学习路线四阶段:入门 → 深入 → 框架 → 工程化,本教程不依赖 Flutter 即可完成前两阶段

练习

题号题目链接知识点
P1000超级玛丽游戏https://www.luogu.com.cn/problem/P1000输出、字符串

题目要求按给定图案逐行输出一幅字符画。这是感受「程序输出由字符串与换行组成」的最直接练习:把每一行图案当作一个字符串,依次输出即可。在 Dart 中可以用多次 print,也可以用多行字符串 ''' 一次输出。