学习链接:https://dart.cn/

Dart 为什么学习

Dart 基础信息

  • Dart 是谷歌出品以及是一个开源、易用、可移植的高效的语言吧,适用于全平台的搭建和使用吧

  • Flutter 的开发依赖于 Dart 语言,Flutter 是一个性能接近原生的跨平台开发框架

An illustration of the targets supported by Dart

  • Dart 主要是针对于客户端进行的一个编译运行时的优化语言,核心用于跨平台的开发实践吧,上图就是从官方进行截取的图吧

Dart 双运行模式

  • 拥有着两种开发模式 AOT(Ahead-Of-Time)编译JIT(Just-In-Time)编译,这两种编译模式核心用于不同的开发阶段,分别是生产阶段和开发阶段的编译模式吧

  • JIT编译

    • 是我们的开发时运行的的编译模式 Flutter run 直接运行的时机吧,默认实现的是开启了 Debug 模式吧,此时表现出来的特性:

      • 是 Dart 代码不会被编译为机器码,而是通过 Dart VM 虚拟机环境实时性的进行解析执行

      • 如果某段代码被多次执行(频繁的修改 Flutter 的UI widgets 逻辑),JIT 便会进行对这段进行热重载的编译为机器码进行缓存,加速后续的执行效果,提高对应的开发效率和体验吧,和前端的 V8 引擎的差不多的讷

    • 核心优势(开发效率优先)

      • 热重载(Hot Reload):这个就是我们的 Flutter 的核心的特性,JIT模式下,修改代码后无需进行重新编译整个应用,只需要进行将变更的 Dart 代码进行增量编译发送给 VM,VM实时性的更新执行逻辑,界面瞬间刷新的特性吧 (JIT允许程序运行时动态替换函数/类的实现,而AOT编译为机器码后就无法实现动态的修改,因此热重载的特性只能在 JIT 模式下实现吧

      • 快速启动特性:开发阶段不需要进行全量的 AOT 编译,Dart VM 直接进行加载代码执行吧,缩短了开发周期

    • 缺点:

      • 运行时的性能略低:因为每次的更新都是需要进行解析执行和即时编译 overhead 会导致 Debug 模式下的性能不如 Release 模式下的的(表现在动画效果的卡顿吧)

      • 依赖于 Dart VM,程序的运行需要 VM 的支持,无法直接生成纯机器码的可执行文件

  • AOT 编译

    • 发布flutter应用的时候 Flutter build apk/ios ,Dart代码通过 dart2aot 工具提前编译为原生机器码(和目标平台架构匹配,ARM x86),最终打包到应用中,运行是无需 Dart VM 解析,直接操作系统执行即可

    • 核心的优势

      • 启动速度快:无需启动 VM 和实时编译,应用瞬间启动

      • 运行效率高:机器码直接执行,省去了解析器和 JIT 编译的开销,复杂逻辑更加流畅吧

      • 更小的体积:无需包含 Dart VM 运行时,减少了应用包的体积

      • 安全性提升:机器码比源码更难逆向破解,一定程度上保护了代码逻辑

    • 局限性就是:AOT编译比较慢,但是也能理解,正常的状态吧,编译了就无法被修改,因为是 Release 版本吧

  • 为什么是双编译模式讷?

    • Flutter 的核心出现背景是统一大前端以及提供 “原生应用性能 + 前端的开发效率和体验”,此时我们的单编译模式就支持不了吧

      • 仅仅是 JIT 编译模式的话:开发效率和体验的的确确好,但是运行性能严重出现问题

      • 若仅用 AOT 编译模式的话:运行性能接近原生,但是每次修改都需要全量编译,迭代效率极低

语言

编译模式

与 Dart 的差异

Java/Kotlin

默认 JIT(Android ART 虚拟机)

运行时依赖 VM,AOT 编译(ART 的 AOT)仅在安装时优化,不如 Dart 全量预编译彻底

JavaScript

解释执行 + JIT(V8 引擎)

无 AOT 模式,运行性能依赖 JIT 优化,整体低于 Dart AOT

C/C++

纯 AOT(编译为机器码)

性能极致,但开发时修改需全量重新编译,迭代效率低

Rust

纯 AOT(编译为机器码)

性能与 C/C++ 相当,且内存安全(所有权模型);无 JIT 模式,开发迭代效率低于 Dart JIT,适合系统级 / 高性能场景

Go

混合模式(编译为机器码 + 运行时)

编译时生成机器码,但运行时依赖 Go 运行时(含垃圾回收等);无 JIT 模式,开发效率高于 C++/Rust,但性能略低于纯 AOT 语言,且热重载支持弱于 Dart JIT

Dart

双模式(JIT 用于开发,AOT 用于发布)

开发阶段 JIT 支持热重载、快速迭代;发布阶段 AOT 保障性能,是 “开发效率 + 运行性能” 的平衡方案,适合跨平台 UI 开发

Dart NativeCode编译对比

语言

运行模式

核心机制概述

与 “热点代码” 的关联

Dart

JIT(开发)+ AOT(发布)双模式

开发时 JIT 动态编译 “热点代码” 并缓存;发布时 AOT 全量预编译为机器码

JIT 阶段通过 “热点探测” 编译高频代码,与 V8 逻辑同源

Rust/C++

纯 AOT(静态编译)

编译期直接生成机器码,运行时无编译过程,依赖操作系统直接执行

无运行时编译,性能极致但开发迭代慢

Go

编译为机器码 + 运行时依赖

编译期生成机器码,但运行时依赖 Go 运行时(含 GC、协程调度),无 JIT 动态编译

无 “热点代码” 编译逻辑,性能介于 AOT 和 JIT 之间

Java

JIT(默认)+ 部分 AOT(ART)

运行时 JVM 解释 + JIT 编译热点代码;Android ART 在安装时对字节码做 AOT 优化

JIT 逻辑与 V8、Dart 类似,但依赖虚拟机(JVM/ART)

JavaScript(V8 引擎)

解释执行 + JIT(V8 特有)

先解释执行,同时 “热点探测器” 识别高频代码,由 “Crankshaft/Turbofan” 编译器编译为机器码缓存

核心就是 “热点代码” 的 JIT 编译,是前端性能优化的关键

  • 深入理解前端v8引擎原理(本博主的出身就是前端,对于 v8 引擎的衷心还是有的,哈哈)

    • V8 引擎原理和 Dart JIT 的逻辑是同源的

    • 解析执行阶段

      • 代码优先由 lgnition 解释器 逐行转换为字节码并且执行,此时执行速度很慢,但是启动快

    • 热点探测阶段

      • V8 内置热点探测器Profiler,持续监控代码的执行频率,当某段代码(函数、循环)被多次执行后,被标记为热点代码

    • JIT 编译阶段

      • 热点代码会被交给 TurboFan 编译器,编译为原生机器码并缓存,后续代码执行时,直接运行机器码,跳过解释过程,性能大幅度提升实现

    • 去优化机制

      • 若热点代码的执行场景发生变化,v8 会触发去优化,将机器码回退为字节码,重新由解释器执行,避免逻辑错误出现

  • Dart JIT 和 V8 区别

    • 相同点:都是运行时识别热点代码,动态编译为机器码缓存,以此实现平衡了启动速度和执行性能

    • 不同点:Dart JIT 是专门为 flutter 开发阶段进行设置的,但是 v8 引擎是 javascript 的生产级的运行时

维度

字节码(Bytecode)

机器码(Machine Code)

定义

虚拟机(VM)可理解的中间代码,二进制形式,与平台无关

计算机 CPU 可直接执行的指令,二进制形式,与平台强相关(如 x86/ARM)

执行方式

需虚拟机解释执行(或 JIT 编译为机器码)

直接由 CPU 加载执行,无需中间层

跨平台性

强(同一份字节码可在不同平台的虚拟机上运行)

弱(x86 机器码无法在 ARM 架构上执行)

性能

中等(解释执行有开销,JIT 后接近机器码)

最高(CPU 直接执行,无额外开销)

体积

较小(比源码大,比机器码小)

较大(与平台相关,指令集冗余)

后续操作

1. 虚拟机解释执行;2. JIT 编译器编译为机器码缓存

直接由操作系统加载到内存,CPU 按地址顺序执行

v8 编译原理

┌───────────────┐    解析    ┌───────────────┐    生成    ┌───────────────┐
│  JavaScript   │ ─────────> │   抽象语法树   │ ─────────> │  Ignition 字节码 │
│    源码       │            │   (AST)       │            │                │
└───────────────┘            └───────────────┘            └───────┬───────┘
                                                                  │
┌──────────────────────────────────────────────────────────────────┘
│
├─ 解释执行(首次执行/非热点代码) ──────────────────────────────┐
│  ┌───────────────┐    逐行解释    ┌───────────────┐           │
│  │  Ignition 字节码 │ ───────────> │  执行结果输出  │           │
│  └───────────────┘                 └───────────────┘           │
└──────────────────────────────────────────────────────────────────┘
│
├─ 热点代码 JIT 编译(高频执行代码) ─────────────────────────────┐
│  ┌───────────────┐    标记热点    ┌───────────────┐           │
│  │  Ignition 字节码 │ ───────────> │  热点探测器    │           │
│  └───────────────┘                 └───────┬───────┘           │
│                                            │                  │
│  ┌───────────────┐    编译为      ┌───────────────┐           │
│  │  机器码缓存    │ <───────────── │  TurboFan 编译器 │          │
│  └───────┬───────┘                 └───────────────┘           │
│          │                                                     │
│          └───────────────────────> 直接执行机器码(跳过解释)   │
└──────────────────────────────────────────────────────────────────┘
│
└─ 去优化机制(类型变化等场景) ──────────────────────────────────┐
   ┌───────────────┐    回退为      ┌───────────────┐            │
   │  机器码缓存    │ ───────────> │  Ignition 字节码 │           │
   └───────────────┘                 └───────────────┘            │
                                                                  │
   (重新进入解释执行流程,避免类型错误导致的逻辑异常)             │
└──────────────────────────────────────────────────────────────────┘

dart JIT|AOT 编译原理

┌───────────────┐    解析    ┌───────────────┐    加载    ┌───────────────┐
│    Dart 源码   │ ─────────> │   抽象语法树   │ ─────────> │  Dart VM 虚拟机 │
│                │            │   (AST)       │            │                │
└───────────────┘            └───────────────┘            └───────┬───────┘
                                                                  │
┌──────────────────────────────────────────────────────────────────┘
│
├─ 初始解释执行 ──────────────────────────────────────────────────┐
│  ┌───────────────┐    逐行解释    ┌───────────────┐            │
│  │  Dart VM 字节码 │ ───────────> │  执行结果输出  │            │
│  └───────────────┘                 └───────────────┘            │
└──────────────────────────────────────────────────────────────────┘
│
├─ 热点代码 JIT 编译 ─────────────────────────────────────────────┐
│  ┌───────────────┐    标记热点    ┌───────────────┐            │
│  │  Dart VM 字节码 │ ───────────> │  热点探测器    │            │
│  └───────────────┘                 └───────┬───────┘            │
│                                            │                   │
│  ┌───────────────┐    编译为      ┌───────────────┐            │
│  │  机器码缓存    │ <───────────── │  JIT 编译器    │            │
│  └───────┬───────┘                 └───────────────┘            │
│          │                                                      │
│          └───────────────────────> 直接执行机器码               │
└──────────────────────────────────────────────────────────────────┘
│
├─ 热重载机制(开发核心特性) ─────────────────────────────────────┐
│  ┌───────────────┐    修改代码    ┌───────────────┐            │
│  │  开发者修改源码 │ ───────────> │  增量编译为    │            │
│  └───────────────┘                 │  新字节码     │            │
│                                    └───────┬───────┘            │
│                                            │                   │
│  ┌───────────────┐    动态替换    ┌───────────────┐            │
│  │  运行时状态保留 │ <───────────── │  VM 替换旧字节码 │         │
│  └───────────────┘                 └───────────────┘            │
│                                                                 │
│  (无需重启应用,瞬间更新UI,保留变量状态)                      │
└──────────────────────────────────────────────────────────────────┘
┌───────────────┐    全量编译    ┌───────────────┐    打包    ┌───────────────┐
│    Dart 源码   │ ─────────────> │  dart2aot 工具 │ ─────────> │  目标平台机器码 │
│                │                │                │            │  (ARM/x86等)  │
└───────────────┘                └───────────────┘            └───────┬───────┘
                                                                      │
┌──────────────────────────────────────────────────────────────────────┘
│
├─ 直接执行(无虚拟机依赖) ──────────────────────────────────────────┐
│  ┌───────────────┐    加载到内存   ┌───────────────┐               │
│  │  目标平台机器码 │ ─────────────> │  CPU 直接执行  │               │
│  └───────────────┘                  └───────┬───────┘               │
│                                             │                      │
│                                             └──────────> 执行结果    │
└──────────────────────────────────────────────────────────────────────┘
│
└─ 核心优势 ──────────────────────────────────────────────────────────┐
   • 启动速度快(无VM初始化和解释步骤)                                │
   • 运行效率高(机器码直接执行,无JIT开销)                           │
   • 安全性强(机器码难以逆向,保护源码逻辑)                           │
└──────────────────────────────────────────────────────────────────────┘

rust/cpp 编译原理

┌─────────────────┐    预处理    ┌─────────────────┐
│  Rust/C++ 源码   │ ───────────> │  预处理后的源码  │  // 处理宏、头文件包含(如C++的#include)
└────────┬────────┘              └────────┬────────┘
         │                                │
         ▼                                ▼
┌─────────────────┐    编译      ┌─────────────────┐
│  预处理后的源码  │ ───────────> │  汇编代码       │  // Rust用rustc、C++用g++/clang,生成人类可读的汇编
└────────┬────────┘              └────────┬────────┘
         │                                │
         ▼                                ▼
┌─────────────────┐    汇编      ┌─────────────────┐
│  汇编代码       │ ───────────> │  目标文件(.o)  │  // 汇编器将汇编转为机器码,生成二进制目标文件
└────────┬────────┘              └────────┬────────┘
         │                                │
         ▼                                ▼
┌─────────────────┐    链接      ┌─────────────────┐
│  目标文件(.o)  │ ───────────> │  可执行文件     │  // 链接器合并多个目标文件,解析符号引用(如函数调用)
└────────┬────────┘              └────────┬────────┘
         │                                │
         ▼                                ▼
┌─────────────────┐    加载执行  ┌─────────────────┐
│  可执行文件     │ ───────────> │  操作系统加载    │  // 直接加载到内存,CPU按机器码指令执行
│  (纯机器码)    │              │  CPU直接执行     │
└─────────────────┘              └─────────────────┘

核心特性:
• 无运行时依赖:机器码直接与操作系统交互,不依赖虚拟机/解释器
• 性能极致:编译期完成所有优化(如常量折叠、循环展开),无运行时开销
• 平台相关:不同架构(x86/ARM)需重新编译,机器码不可跨平台
• 内存管理:C++需手动管理(new/delete),Rust通过所有权模型自动管理(零开销)

go 编译原理

┌─────────────────┐    解析与     ┌─────────────────┐
│  Go 源码        │ ─── 类型检查 ─→ │  抽象语法树     │  // go build 命令触发,检查语法和类型错误
└────────┬────────┘              └────────┬────────┘
         │                                │
         ▼                                ▼
┌─────────────────┐    中间代码   ┌─────────────────┐
│  抽象语法树     │ ─── 生成 ─────→ │  SSA 中间代码   │  // 静态单赋值形式,便于优化(如逃逸分析、内联)
└────────┬────────┘              └────────┬────────┘
         │                                │
         ▼                                ▼
┌─────────────────┐    机器码     ┌─────────────────┐
│  SSA 中间代码   │ ─── 生成 ─────→ │  目标平台机器码  │  // 直接编译为机器码,包含Go运行时(runtime)
└────────┬────────┘              └────────┬────────┘
         │                                │
         ▼                                ▼
┌─────────────────┐    打包      ┌─────────────────┐
│  机器码+运行时   │ ───────────> │  可执行文件     │  // 单文件打包,无需额外依赖(静态链接)
└────────┬────────┘              └────────┬────────┘
         │                                │
         ▼                                ▼
┌─────────────────┐    加载执行  ┌─────────────────┐
│  可执行文件     │ ───────────> │  启动Go运行时   │  // 初始化GC、协程调度器(GPM模型)
│                 │              │  执行机器码     │  // 业务逻辑机器码在运行时调度下执行
└─────────────────┘              └─────────────────┘

核心特性:
• 编译速度快:单阶段编译,无字节码过渡,直接生成机器码
• 运行时轻量:包含GC和协程调度,但性能接近纯AOT(开销远低于JVM)
• 跨平台编译:支持交叉编译(如在Windows编译Linux可执行文件)
• 协程高效:Goroutine轻量(KB级栈),运行时调度器高效管理并发

java/kotilin 编译原理

┌─────────────────┐    编译      ┌─────────────────┐
│  Java/Kotlin源码 │ ───────────> │  字节码(.class)│  // Java用javac,Kotlin用kotlinc,生成与平台无关的字节码
└────────┬────────┘              └────────┬────────┘
         │                                │
         ▼                                ▼
┌─────────────────┐    加载      ┌─────────────────┐
│  字节码(.class)│ ───────────> │  JVM/ART 虚拟机  │  // 类加载器(ClassLoader)加载字节码到内存
└────────┬────────┘              └────────┬────────┘
         │                                │
         ├───────────────┬────────────────┘
         │               │
         ▼               ▼
┌─────────────────┐  ┌─────────────────┐
│  解释执行       │  │  JIT 编译(热点代码)│
│  (初始执行)    │  │                 │
│  ┌───────────┐  │  ┌─────────────┐  │
│  │ 字节码逐行 │  │  │ 热点探测器标记 │  │
│  │ 解释为机器码│  │  │ 高频执行代码  │  │
│  └───────────┘  │  └──────┬──────┘  │
└─────────────────┘         │         │
                            ▼         │
                       ┌─────────────┐│
                       │ JIT编译器将 ││
                       │ 字节码转为  ││
                       │ 机器码缓存  ││
                       └──────┬──────┘│
                              │      │
                              ▼      ▼
                      ┌─────────────────┐
                      │  执行结果输出   │  // 非热点代码走解释,热点代码直接执行机器码
                      └─────────────────┘

// Android ART 额外流程(发布阶段):
┌─────────────────┐    安装时    ┌─────────────────┐
│  字节码(.dex)  │ ─── AOT ───> │  机器码(.oat)  │  // 预编译字节码为机器码,启动更快(替代部分JIT)
└─────────────────┘  编译        └─────────────────┘

核心特性:
• 跨平台性强:一次编译(字节码),多平台运行(依赖JVM/ART)
• 动态性支持:反射、动态代理(基于字节码运行时修改)
• 性能平衡:解释执行启动快,JIT/AOT优化高频代码性能
• 内存管理:自动GC(分代回收、G1等算法),无需手动控制

Dart WebCode 编译原理

一、Dart 面向 Web 平台的核心定位

Dart 并非仅服务于 Flutter 移动端,在 Web 领域,它通过编译为 JavaScriptWebAssembly,实现了在浏览器生态中的运行能力。这让开发者可以用 Dart 语言统一前端(Web)和移动端(Flutter)的开发,同时借助 Web 生态的能力(如浏览器 API、前端框架)。

二、Dart Web 的三种编译模式(核心逻辑)

Dart 为 Web 场景设计了三种差异化的编译策略,分别服务于开发阶段生产阶段

1. 增量式 JavaScript 编译器(开发阶段:热重载)
  • 作用:专为开发效率设计,支持热重载(Hot Reload)

  • 原理

    当你修改 Dart 代码时,编译器仅增量编译变更的部分,而非全量编译。编译后的 JavaScript 代码会实时注入浏览器,界面瞬间刷新且保留运行时状态(如变量值、组件状态)。

  • 场景:本地开发调试,大幅缩短 “修改→编译→预览” 的循环时间。

2. 生产环境优化的 JavaScript 编译器(发布阶段:性能与体积)
  • 作用:将 Dart 代码编译为可部署的高性能 JavaScript,用于线上环境。

  • 核心优化

    • Tree Shaking:消除未使用的代码(类似前端工程化中的无用代码剔除),大幅减小包体积。

    • 代码压缩:混淆变量名、移除注释和空格,进一步降低资源体积。

    • 运行时优化:编译后的 JavaScript 代码针对 V8 等引擎做了适配,执行效率接近手写 JS。

  • 场景:线上 Web 应用发布,平衡性能和加载速度。

3. WebAssembly (WasmGC) 编译器(高性能场景:替代 JavaScript)
  • 技术背景:WebAssembly 是一种二进制指令格式,可在浏览器中高效运行,性能远超 JavaScript(尤其在计算密集型场景)。“WasmGC” 是支持垃圾回收的 WebAssembly 扩展,让带 GC 的语言(如 Dart)能无缝编译为 Wasm。

  • 编译逻辑

    Dart 代码被编译为 Wasm 二进制文件,在浏览器中通过 WebAssembly 引擎直接执行,绕过 JavaScript 解释 / 编译环节,性能更接近原生应用。

  • 场景

    适用于计算密集型 Web 应用(如在线编辑器、数据可视化、游戏引擎),或对性能要求极高的前端场景。

三、技术原理与生态关联

1. 与 JavaScript 生态的融合

Dart 编译为 JavaScript 后,可直接调用浏览器 API(如 DOMFetch)和前端框架生态(如与 React、Vue 集成)。例如,你可以用 Dart 写业务逻辑,再通过编译后的 JS 与现有前端系统交互。

2. WebAssembly 的优势与局限
  • 优势

    • 性能极致:二进制指令直接在浏览器引擎中执行,无 JS 解释器的开销。

    • 语言无关:Dart、Rust、C++ 等语言均可编译为 Wasm,实现跨语言协作。

  • 局限

    • 生态成熟度:WebAssembly 对 DOM 操作、浏览器 API 的支持不如 JavaScript 原生友好,适合 “计算密集型逻辑” 而非 “界面交互逻辑”。

    • 调试复杂度:Wasm 二进制代码的调试难度高于 JavaScript。

四、实际应用与工具链

1. 开发工具
  • webdev 工具:Dart 官方的 Web 开发 CLI,支持热重载、编译为 JS/Wasm。

    • 开发调试:webdev serve 启动本地服务,实时预览并热重载。

    • 生产编译:webdev build 生成优化后的 JS 或 Wasm 产物。

  • dart compile js 命令:手动触发 Dart 到 JavaScript 的编译,可指定优化级别。

2. 典型场景示例
  • 跨端统一开发:用 Dart 同时开发 Flutter 移动端和 Web 前端,共享业务逻辑,仅需适配界面层。

  • 高性能 Web 应用:如在线 IDE(代码编译、运行需大量计算)、3D 渲染引擎,用 Dart 编译为 Wasm 提升执行效率。

五、与其他前端技术的对比

技术

性能

开发效率

生态成熟度

适用场景

Dart → JS

接近手写 JS

高(Dart 语法 + 热重载)

高(兼容 JS 生态)

通用 Web 应用、跨端统一

Dart → Wasm

极致

中(Dart 语法)

中(Wasm 生态)

计算密集型逻辑、高性能工具

原生 JavaScript

中(依赖引擎优化)

中(纯 JS 语法)

极高

界面交互、生态集成

Rust → Wasm

极致

低(Rust 语法复杂度)

底层工具、性能敏感场景

总结

Dart 在 Web 平台的三种编译模式,是 “开发效率” 与 “运行性能” 的分层解决方案:

  • 开发阶段用增量 JS 编译 + 热重载提升迭代速度;

  • 生产阶段用优化 JS 覆盖通用 Web 场景;

  • 高性能场景用WasmGC 突破 JavaScript 的性能上限。

核心主要是围绕的是底层原理进行讲解吧,没有涉及到 dart 的任何的语法代码等等吧