学习链接:https://dart.cn/
Dart 为什么学习
Dart 基础信息
Dart 是谷歌出品以及是一个开源、易用、可移植的高效的语言吧,适用于全平台的搭建和使用吧
Flutter 的开发依赖于 Dart 语言,Flutter 是一个性能接近原生的跨平台开发框架
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 NativeCode编译对比
深入理解前端v8引擎原理(本博主的出身就是前端,对于 v8 引擎的衷心还是有的,哈哈)
V8 引擎原理和 Dart JIT 的逻辑是同源的
解析执行阶段
代码优先由 lgnition 解释器 逐行转换为字节码并且执行,此时执行速度很慢,但是启动快
热点探测阶段
V8 内置热点探测器Profiler,持续监控代码的执行频率,当某段代码(函数、循环)被多次执行后,被标记为热点代码
JIT 编译阶段
热点代码会被交给 TurboFan 编译器,编译为原生机器码并缓存,后续代码执行时,直接运行机器码,跳过解释过程,性能大幅度提升实现
去优化机制
若热点代码的执行场景发生变化,v8 会触发去优化,将机器码回退为字节码,重新由解释器执行,避免逻辑错误出现
Dart JIT 和 V8 区别
相同点:都是运行时识别热点代码,动态编译为机器码缓存,以此实现平衡了启动速度和执行性能
不同点:Dart JIT 是专门为 flutter 开发阶段进行设置的,但是 v8 引擎是 javascript 的生产级的运行时
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 领域,它通过编译为 JavaScript 或 WebAssembly,实现了在浏览器生态中的运行能力。这让开发者可以用 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(如 DOM、Fetch)和前端框架生态(如与 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 在 Web 平台的三种编译模式,是 “开发效率” 与 “运行性能” 的分层解决方案:
开发阶段用增量 JS 编译 + 热重载提升迭代速度;
生产阶段用优化 JS 覆盖通用 Web 场景;
高性能场景用WasmGC 突破 JavaScript 的性能上限。
核心主要是围绕的是底层原理进行讲解吧,没有涉及到 dart 的任何的语法代码等等吧