目录
- 概述
- 整体架构
- 初始化与加载流程
- JavaScript 引擎与执行上下文
- DOM 树构建
- CSS 解析与样式计算
- FFI 通信机制
- Widget 适配层
- 布局与渲染
- 事件处理与交互
- 完整流程图
概述
WebF 是一个 W3C/WHATWG 标准兼容的 Web 运行时,它允许你使用 React、Vue、Svelte 等主流 前端框架 构建原生 Flutter 应用。它并不是一个浏览器,而是一个为应用构建优化的运行时。与 WebView 的最大区别在于,WebF 拥有基于 Flutter 渲染管线实现的自定义渲染引擎,能够达到原生级别的性能。
核心特点
- 不是浏览器:WebF 是应用运行时,针对应用构建进行了优化
- 自定义渲染引擎:基于 Flutter 的 RenderObject 系统实现 CSS 盒模型
- 双向互操作:JavaScript 可直接调用 Dart/Flutter 原生能力
- 持久化 JavaScript 上下文:应用状态在页面导航间保持
整体架构
WebF 采用分层架构设计,主要包含以下几个核心层:

目录结构对应
bridge/: C++ 代码,提供 JavaScript 运行时和 DOM API 实现
webf/: Dart 代码,实现 DOM/CSS 和 Flutter 上的布局/绘制
cli/: WebF CLI,用于生成 React/Vue 绑定代码
初始化与加载流程
3.1 WebFController 初始化
WebF 的入口点是 WebFController,它负责管理整个 Web 运行时的生命周期:
// 1. 初始化 WebFControllerManager (单例)
WebFControllerManager.instance.initialize(
WebFControllerManagerConfig(
maxAliveInstances: 5,
maxAttachedInstances: 3,
),
);
// 2. 创建并添加控制器(支持预渲染)
WebFControllerManager.instance.addWithPrerendering(
name: 'home',
createController: () => WebFController(),
bundle: WebFBundle.fromUrl('https://example.com/'),
);
// 3. 在 Widget 树中使用
WebF.fromControllerName(
controllerName: 'home',
loadingWidget: CircularProgressIndicator(),
)
3.2 控制器创建过程
当 WebFController 被创建时,会执行以下初始化步骤:
// webf/lib/src/launcher/controller.dart
WebFController({...}) {
// 1. 记录构造阶段
_loadingState.recordPhase(LoadingState.phaseConstructor, ...);
// 2. 初始化预加载 Bundle
_initializePreloadBundle();
// 3. 创建 Cookie 管理器
cookieManager = CookieManager();
// 4. 创建 ViewController(核心视图控制器)
_view = WebFViewController(
enableDebug: enableDebug,
rootController: this,
runningThread: this.runningThread!,
...
);
}
3.3 ViewController 初始化
WebFViewController 是连接 C++ Bridge 和 Dart 层的关键组件:
// webf/lib/src/launcher/view_controller.dart
Future<void> initialize() async {
// 1. 初始化 C++ Bridge,获取 contextId
_contextId = await initBridge(this, runningThread, enableBlink);
// 2. 设置观察者(生命周期、内存等)
_setupObserver();
// 3. 定义内置元素类型
defineBuiltInElements();
}
JavaScript 引擎与执行上下文
4.1 QuickJS 引擎
WebF 使用 QuickJS 作为 JavaScript 引擎,它是一个小型、可嵌入的引擎,支持 ES2020 标准。
// bridge/core/executing_context.cc
ExecutingContext::ExecutingContext(...) {
// 1. 初始化时间原点(用于 performance API)
time_origin_ = std::chrono::system_clock::now();
// 2. 获取 JavaScript 上下文
JSContext* ctx = script_state_.ctx();
global_object_ = JS_GetGlobalObject(script_state_.ctx());
// 3. 开启垃圾回收
JS_TurnOnGC(script_state_.runtime());
// 4. 设置上下文不透明指针(用于在 JS 回调中获取 ExecutingContext)
JS_SetContextOpaque(ctx, this);
// 5. 设置 Promise 拒绝追踪器
JS_SetHostPromiseRejectionTracker(script_state_.runtime(), promiseRejectTracker, nullptr);
// 6. 注册所有内置原生绑定
InstallBindings(this);
// 7. 安装 document 对象
InstallDocument();
// 8. 绑定 window 全局对象
InstallGlobal();
// 9. 安装 performance API
InstallPerformance();
InstallNativeLoader();
// 10. 设置 ES 模块加载器
JS_SetModuleLoaderFunc(script_state_.runtime(), ModuleNormalizeName, ModuleLoader, this);
// 11. 初始化 JavaScript Polyfill
EvaluateByteCode(bridge_polyfill, bridge_polyfill_size);
}
4.2 Bindings 安装
WebF 在 C++ 层实现了完整的 Web API 绑定:
// bridge/bindings/qjs/binding_initializer.cc
void InstallBindings(ExecutingContext* context){
// 必须按照继承顺序安装
// 例如: Node 继承自 EventTarget,所以 EventTarget 必须先安装
QJSWindowOrWorkerGlobalScope::Install(context);
QJSLocation::Install(context);
QJSEventTarget::Install(context);
QJSWindow::Install(context);
QJSEvent::Install(context);
QJSNode::Install(context);
QJSDocument::Install(context);
QJSElement::Install(context);
QJSHTMLElement::Install(context);
// ... 安装所有 HTML 元素 (div, span, img, etc.)
// CSS 相关
QJSCSSStyleDeclaration::Install(context);
QJSCSSStyleSheet::Install(context);
// SVG 相关
QJSSVGElement::Install(context);
QJSSVGSVGElement::Install(context);
// 其他 Web API
QJSMutationObserver::Install(context);
QJSIntersectionObserver::Install(context);
QJSPerformance::Install(context);
// ...
}
4.3 JavaScript 执行
JavaScript 代码的执行流程:
// bridge/core/executing_context.cc
bool ExecutingContext::EvaluateJavaScript(
const char* code,
size_t code_len,
uint8_t** parsed_bytecodes,
uint64_t* bytecode_len,
const char* sourceURL,
int startLine,
HTMLScriptElement* script_element){
// 1. 检查是否允许执行脚本
if (ScriptForbiddenScope::IsScriptForbidden()) {
return false;
}
// 2. 初始化 document URL(用于相对路径解析)
MaybeInitializeDocumentURLFromSourceURL(sourceURL);
// 3. 编译并执行 JavaScript
val = JS_EvalFunction(script_state_.ctx(), obj);
// 4. 处理异常
bool success = HandleException(&val);
// 5. 排空微任务队列
DrainMicrotasks();
return success;
}
DOM 树构建
5.1 双层 DOM 架构
WebF 采用独特的双层 DOM 架构,将逻辑与渲染分离:
- C++ 层:实现 W3C 标准的 DOM API,处理 JavaScript 交互
- Dart 层:负责实际的渲染和布局

5.2 C++ 层 DOM 创建
当 JavaScript 调用 document.createElement() 时,在 C++ 层发生:
// bridge/core/dom/document.cc
HTMLElement* Document::createElement(const AtomicString& name, ExceptionState& exception_state){
const AtomicString& local_name = name.LowerASCII();
// 1. 验证标签名
if (!IsValidName(local_name)) {
exception_state.ThrowException(...);
return nullptr;
}
// 2. 通过工厂创建对应的 HTML 元素
if (auto* element = HTMLElementFactory::Create(local_name, *this)) {
return element;
}
// 3. 处理自定义元素
// ...
}
5.3 Dart 层 DOM 创建
相应的,Dart 层通过元素注册表创建对应的 DOM 元素:
// webf/lib/src/dom/element_registry.dart
Element createElement(String name, [BindingContext? context]) {
// 1. 查找元素创建器
ElementCreator? creator = _htmlRegistry[name]
?? _overrideWidgetElements[name]
?? _widgetElements[name];
Element element;
if (creator == null) {
element = _UnknownHTMLElement(context);
} else {
element = creator(context);
}
// 2. 设置 tagName
element.tagName = name;
element.namespaceURI = htmlElementUri;
return element;
}
CSS 解析与样式计算
6.1 CSS 解析器
WebF 使用自定义的 CSS 解析器,遵循 CSS 规范:
// bridge/core/css/parser/css_parser.cc
std::shared_ptr<const CSSPropertyValueSet> CSSParser::ParseInlineStyleDeclaration(
const std::string& style_string,
CSSParserContext* context){
// 解析内联样式字符串为属性值集合
return CSSParserImpl::ParseInlineStyleDeclaration(style_string, context);
}
6.2 样式解析器 (StyleResolver)
样式解析器负责收集和应用匹配的 CSS 规则:
// bridge/core/css/resolver/style_resolver.cc
std::shared_ptr<const ComputedStyle> StyleResolver::ResolveStyle(
Element* element,
const StyleRecalcContext& style_recalc_context,
const StyleRequest& style_request){
// 1. 创建样式解析状态
StyleResolverState state(GetDocument(), *element, style_recalc_context, style_request);
// 2. 创建样式级联对象
StyleCascade cascade(state);
// 3. 应用基础样式
ApplyBaseStyle(element, style_recalc_context, style_request, state, cascade);
// 4. 返回计算后的样式
return state.TakeComputedStyle();
}
6.3 样式级联 (StyleCascade)
样式级联负责处理 CSS 优先级和继承的复杂逻辑:
// bridge/core/css/resolver/style_cascade.cc
void StyleCascade::Apply(CascadeFilter filter){
// 1. 按优先级排序所有匹配的 CSS 声明
// 2. 应用用户代理样式
// 3. 应用用户样式
// 4. 应用作者样式
// 5. 处理 !important 规则
// 6. 处理继承属性
}
6.4 计算样式 (ComputedStyle)
计算样式是最终应用到元素的、解析完毕的样式值:
// bridge/core/css/computed_style.h
class ComputedStyle {
// 盒模型属性
Length width_, height_;
Length margin_top_, margin_right_, margin_bottom_, margin_left_;
Length padding_top_, padding_right_, padding_bottom_, padding_left_;
// 布局属性
EDisplay display_;
EPosition position_;
EFlexDirection flex_direction_;
// 视觉属性
Color background_color_;
Color color_;
// 字体属性
FontDescription font_description_;
// ... 更多 CSS 属性
};
FFI 通信机制
7.1 UICommand 系统
UICommand 是 C++ 和 Dart 之间通信的核心机制,它实现了高效的批量操作传输。

7.2 UICommand 类型
系统定义了一系列命令类型来涵盖所有 DOM 操作:
// webf/lib/src/bridge/ui_command.dart
enum UICommandType {
createElement, // 创建元素
createTextNode, // 创建文本节点
createComment, // 创建注释节点
createDocument, // 创建文档
createWindow, // 创建窗口
disposeBindingObject, // 释放绑定对象
addEvent, // 添加事件监听
removeEvent, // 移除事件监听
insertAdjacentNode, // 插入相邻节点
removeNode, // 移除节点
cloneNode, // 克隆节点
setStyle, // 设置样式
clearStyle, // 清除样式
setInlineStyle, // 设置内联样式
setAttribute, // 设置属性
removeAttribute, // 移除属性
// ... 更多命令类型
}
7.3 UICommand 执行
Dart 层接收并执行来自 C++ 的批量 UICommand:
// webf/lib/src/bridge/ui_command.dart
void execUICommands(WebFViewController view, List<UICommand> commands) {
for (UICommand command in commands) {
switch (command.type) {
case UICommandType.createElement:
// 创建 Dart DOM 元素
view.createElement(nativePtr.cast<NativeBindingObject>(), command.args);
break;
case UICommandType.setStyle:
// 设置元素样式
BindingObject bindingObject = view.getBindingObject(nativePtr);
if (bindingObject is Element) {
bindingObject.setInlineStyle(command.args[0], command.args[1]);
}
break;
case UICommandType.insertAdjacentNode:
// 插入节点到 DOM 树
Node parent = view.getBindingObject(parentPtr) as Node;
Node child = view.getBindingObject(childPtr) as Node;
parent.insertBefore(child, referenceNode);
break;
// ... 处理其他命令类型
}
}
}
7.4 NativeBindingObject
NativeBindingObject 作为桥梁,将 C++ 和 Dart 对象关联起来:
// webf/lib/src/foundation/binding.dart
abstract class BindingObject {
// 指向 C++ 对象的指针
Pointer<NativeBindingObject>? pointer;
// 绑定上下文
BindingContext? _bindingContext;
// 调用 C++ 方法
dynamic invokeBindingMethod(String method, List args) {
return _invokeBindingMethod(pointer!, method.toNativeUtf8(), args);
}
}
WebF 使用 Widget 适配器模式将标准的 DOM 元素无缝转换为 Flutter Widget:
// webf/lib/src/dom/element_widget_adapter.dart
mixin ElementAdapterMixin on Element {
@override
flutter.Widget toWidget({Key? key}) {
return WebFElementWidget(this as Element, key: key ?? (this as Element).key);
}
}
WebFElementWidget 是连接 DOM 树和 Flutter Widget 系统的核心组件:
// webf/lib/src/dom/element_widget_adapter.dart
class WebFElementWidget extends flutter.StatefulWidget{
final Element element;
const WebFElementWidget(this.element, {Key? key}) : super(key: key);
@override
flutter.State<WebFElementWidget> createState() => _WebFElementWidgetState();
}
class _WebFElementWidgetState extends flutter.State<WebFElementWidget> {
@override
flutter.Widget build(flutter.BuildContext context) {
// 1. 获取元素的渲染对象
RenderBoxModel? renderBoxModel = widget.element.renderBoxModel;
// 2. 构建子元素 Widget 列表
List<flutter.Widget> children = [];
for (Node child in widget.element.childNodes) {
if (child is Element) {
children.add(child.toWidget());
} else if (child is TextNode) {
children.add(child.toWidget());
}
}
// 3. 返回包装后的 Widget
return WebFRenderObjectWidget(
element: widget.element,
children: children,
);
}
}
最终,Widget 需要创建出具体的 RenderObject 来进行布局和绘制:
// webf/lib/src/dom/element_widget_adapter.dart
class WebFRenderObjectWidget extends flutter.MultiChildRenderObjectWidget{
final Element element;
@override
flutter.RenderObject createRenderObject(flutter.BuildContext context) {
// 创建对应的 RenderBoxModel
return element.createRenderer();
}
@override
void updateRenderObject(flutter.BuildContext context, RenderBoxModel renderObject) {
// 更新渲染对象的样式
element.updateRenderObject(renderObject);
}
}
布局与渲染
9.1 RenderBoxModel 层次结构
WebF 基于 Flutter 的 RenderBox 实现了完整的 CSS 盒模型渲染系统:

9.2 RenderBoxModel 核心实现
RenderBoxModel 是所有 WebF 渲染对象的基类:
// webf/lib/src/rendering/box_model.dart
abstract class RenderBoxModel extends RenderBox{
// 关联的 DOM 元素
Element? element;
// 渲染样式
RenderStyle? renderStyle;
// 盒模型尺寸
double? _contentWidth;
double? _contentHeight;
@override
void performLayout() {
// 1. 计算盒模型尺寸
_computeBoxSize();
// 2. 布局子元素
_layoutChildren();
// 3. 设置最终尺寸
size = Size(
renderStyle!.width.computedValue,
renderStyle!.height.computedValue,
);
}
@override
void paint(PaintingContext context, Offset offset) {
// 1. 绘制背景
_paintBackground(context, offset);
// 2. 绘制边框
_paintBorder(context, offset);
// 3. 绘制子元素
_paintChildren(context, offset);
// 4. 绘制轮廓
_paintOutline(context, offset);
}
}
9.3 Flow 布局 (Block/Inline)
RenderFlowLayout 实现了标准的流式布局(块级和内联级):
// webf/lib/src/rendering/flow.dart
class RenderFlowLayout extends RenderBoxModel{
@override
void performLayout() {
// 5 步布局算法
// 步骤 1: 计算自身约束
BoxConstraints constraints = _computeConstraints();
// 步骤 2: 布局块级子元素
_layoutBlockChildren(constraints);
// 步骤 3: 布局内联子元素(行盒)
_layoutInlineChildren(constraints);
// 步骤 4: 处理浮动元素
_layoutFloatChildren(constraints);
// 步骤 5: 计算最终尺寸
_computeFinalSize();
}
}
9.4 Flex 布局
RenderFlexLayout 实现了 CSS Flexbox 布局模型:
// webf/lib/src/rendering/flex.dart
class RenderFlexLayout extends RenderBoxModel{
@override
void performLayout() {
// 1. 确定主轴和交叉轴
Axis mainAxis = renderStyle!.flexDirection.isRow ? Axis.horizontal : Axis.vertical;
// 2. 计算 flex 基础尺寸
_computeFlexBasis();
// 3. 分配剩余空间(flex-grow)
_distributeFlexGrow();
// 4. 收缩溢出空间(flex-shrink)
_distributeFlexShrink();
// 5. 对齐子元素(justify-content, align-items)
_alignChildren();
}
}
事件处理与交互
10.1 事件系统架构
WebF 的事件系统将 Flutter 的原生手势转换为标准的 Web DOM 事件:

10.2 GestureDispatcher
GestureDispatcher 负责将 Flutter 手势事件转换为 DOM 事件:
// webf/lib/src/gesture/gesture_dispatcher.dart
class GestureDispatcher{
final Element element;
void handleTapDown(TapDownDetails details) {
// 1. 创建 DOM 事件
MouseEvent event = MouseEvent(
'mousedown',
MouseEventInit(
clientX: details.localPosition.dx,
clientY: details.localPosition.dy,
button: 0,
),
);
// 2. 分发事件到 DOM 树
element.dispatchEvent(event);
}
void handleTap(TapUpDetails details) {
// 创建并分发 click 事件
MouseEvent clickEvent = MouseEvent('click', ...);
element.dispatchEvent(clickEvent);
}
}
10.3 事件传播
事件在 DOM 树中遵循标准的捕获-目标-冒泡三个阶段传播:
// webf/lib/src/dom/event_target.dart
abstract class EventTarget{
void dispatchEvent(Event event) {
// 1. 捕获阶段 (从 window 到 target)
_dispatchEventAtCapturing(event);
// 2. 目标阶段
_dispatchEventAtTarget(event);
// 3. 冒泡阶段 (从 target 到 window)
if (event.bubbles) {
_dispatchEventAtBubbling(event);
}
}
void addEventListener(String type, EventListener listener, {bool capture = false}) {
// 注册事件监听器
_eventListeners[type] ??= [];
_eventListeners[type]!.add(EventListenerEntry(listener, capture));
}
}
10.4 事件回调到 JavaScript
当事件触发时,系统会调用相应的 JavaScript 或 Dart 回调:
// webf/lib/src/dom/event_target.dart
void _invokeEventListener(Event event, EventListener listener) {
if (listener is JSEventListener) {
// 调用 JavaScript 回调
listener.invoke(event);
} else if (listener is DartEventListener) {
// 调用 Dart 回调
listener.callback(event);
}
}
完整流程图
11.1 从加载到渲染的完整流程
┌─────────────────────────────────────────────────────────────────────────────┐
│ 1. 初始化阶段 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Flutter App 启动 │
│ │ │
│ ▼ │
│ WebFControllerManager.initialize() │
│ │ │
│ ▼ │
│ WebFController 创建 │
│ │ │
│ ▼ │
│ WebFViewController.initialize() │
│ │ │
│ ├──▶ initBridge() ──▶ 创建 C++ ExecutingContext │
│ │ │ │
│ │ ├──▶ 初始化 QuickJS 运行时 │
│ │ ├──▶ InstallBindings() (安装 DOM/CSS API) │
│ │ ├──▶ InstallDocument() (创建 document 对象) │
│ │ └──▶ InstallGlobal() (创建 window 对象) │
│ │ │
│ └──▶ defineBuiltInElements() (注册 Dart 元素工厂) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 2. 资源加载阶段 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ WebFBundle.load() │
│ │ │
│ ├──▶ 加载 HTML 内容 │
│ │ │
│ ▼ │
│ HTMLParser.parseHTML() │
│ │ │
│ ├──▶ Gumbo 解析 HTML 字符串 │
│ ├──▶ 遍历 Gumbo 节点树 │
│ └──▶ 创建 C++ DOM 节点 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 3. DOM 构建阶段 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ C++ DOM 操作 │
│ │ │
│ ├──▶ document.createElement('div') │
│ │ │ │
│ │ ▼ │
│ │ HTMLElementFactory::Create() │
│ │ │ │
│ │ ▼ │
│ │ 生成 UICommand::createElement │
│ │ │
│ ├──▶ element.appendChild(child) │
│ │ │ │
│ │ ▼ │
│ │ 生成 UICommand::insertAdjacentNode │
│ │ │
│ └──▶ element.style.color = 'red' │
│ │ │
│ ▼ │
│ 生成 UICommand::setStyle │
│ │
│ UICommandBuffer 收集所有命令 │
│ │ │
│ ▼ │
│ 批量发送到 Dart 层 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 4. Dart DOM 同步阶段 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ execUICommands() │
│ │ │
│ ├──▶ UICommandType.createElement │
│ │ │ │
│ │ ▼ │
│ │ ElementRegistry.createElement() │
│ │ │ │
│ │ ▼ │
│ │ 创建 Dart Element 实例 │
│ │ │
│ ├──▶ UICommandType.insertAdjacentNode │
│ │ │ │
│ │ ▼ │
│ │ parent.insertBefore(child, ref) │
│ │ │
│ └──▶ UICommandType.setStyle │
│ │ │
│ ▼ │
│ element.style.setProperty() │
│ │ │
│ ▼ │
│ 标记需要重新布局 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 5. Widget 构建阶段 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ document.documentElement.toWidget() │
│ │ │
│ ▼ │
│ WebFElementWidget 创建 │
│ │ │
│ ├──▶ 递归构建子元素 Widget │
│ │ │
│ ▼ │
│ WebFRenderObjectWidget │
│ │ │
│ ▼ │
│ createRenderObject() │
│ │ │
│ ▼ │
│ element.createRenderer() │
│ │ │
│ ├──▶ display: block → RenderFlowLayout │
│ ├──▶ display: flex → RenderFlexLayout │
│ ├──▶ display: inline → RenderInline │
│ └──▶ ... │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 6. 布局阶段 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Flutter 布局管线触发 │
│ │ │
│ ▼ │
│ RenderBoxModel.performLayout() │
│ │ │
│ ├──▶ 解析 CSS 约束 (width, height, margin, padding) │
│ │ │
│ ├──▶ 布局子元素 │
│ │ │ │
│ │ ├──▶ Block 布局: 垂直堆叠 │
│ │ ├──▶ Inline 布局: 行盒排列 │
│ │ ├──▶ Flex 布局: 弹性分配 │
│ │ └──▶ Float 布局: 浮动定位 │
│ │ │
│ └──▶ 计算最终尺寸和位置 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 7. 绘制阶段 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ RenderBoxModel.paint() │
│ │ │
│ ├──▶ 绘制背景 (background-color, background-image) │
│ ├──▶ 绘制边框 (border) │
│ ├──▶ 绘制阴影 (box-shadow) │
│ ├──▶ 绘制子元素 │
│ └──▶ 绘制轮廓 (outline) │
│ │
│ Flutter 合成层 │
│ │ │
│ ▼ │
│ Skia/Impeller 渲染到屏幕 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 8. 交互阶段 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 用户触摸屏幕 │
│ │ │
│ ▼ │
│ Flutter GestureDetector │
│ │ │
│ ▼ │
│ GestureDispatcher.handleTap() │
│ │ │
│ ▼ │
│ 创建 MouseEvent/TouchEvent │
│ │ │
│ ▼ │
│ element.dispatchEvent() │
│ │ │
│ ├──▶ 捕获阶段 (window → target) │
│ ├──▶ 目标阶段 │
│ └──▶ 冒泡阶段 (target → window) │
│ │
│ 调用 JavaScript 事件处理函数 │
│ │ │
│ ▼ │
│ JavaScript 修改 DOM │
│ │ │
│ ▼ │
│ 重复步骤 3-7 (增量更新) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
总结
核心设计理念
- 双层 DOM 架构:C++ 层处理 JavaScript 交互和 Web 标准兼容性,Dart 层负责渲染和布局
- 命令缓冲机制:通过 UICommand 批量传输 DOM 操作,减少 FFI 调用开销
- Widget 适配模式:将 DOM 元素无缝转换为 Flutter Widget,复用 Flutter 渲染管线
- 自定义渲染引擎:基于 RenderBoxModel 实现完整的 CSS 盒模型,支持 Block、Flex 等布局
性能优化策略
- 批量 UICommand:收集一帧内的所有 DOM 操作,批量传输到 Dart 层
- 增量更新:只更新变化的 DOM 节点和样式
- 样式缓存:ComputedStyle 缓存避免重复计算
- 布局优化:脏标记机制,只重新布局受影响的子树
与 WebView 的区别
| 特性 |
WebF |
WebView |
| 渲染引擎 |
Flutter (Skia/Impeller) |
系统浏览器引擎 |
| JavaScript 引擎 |
QuickJS (嵌入式) |
V8/JavaScriptCore |
| 原生互操作 |
直接 FFI 调用 |
JavaScript Bridge |
| 启动速度 |
快 (< 100ms) |
慢 (> 500ms) |
| 内存占用 |
低 |
高 |
| 自定义能力 |
完全可控 |
受限 |
WebF vs WebView 性能分析
基于技术原理和架构差异,我们来详细分析两者在不同设备上的性能表现:
核心架构差异
| 组件 |
WebF |
WebView |
| JavaScript 引擎 |
QuickJS (解释器) |
V8/JavaScriptCore (JIT 编译) |
| 渲染引擎 |
Flutter Skia/Impeller |
Chromium/WebKit |
| 二进制大小 |
~5-10MB |
~50-100MB (系统自带) |
| 内存基础开销 |
~40-80MB |
~150-300MB |
中高低设备性能对比
低端设备 (RK3566Pro, 2GB RAM, Cortex-A55)
| 指标 |
WebF |
WebView |
胜出 |
| 冷启动时间 |
150-300ms |
800-2000ms |
✅ WebF (3-6x) |
| 内存占用 |
40-80MB |
150-300MB |
✅ WebF (关键优势!) |
| JavaScript 执行 |
基准 |
理论快 5-15x* |
⚠️ 混合 |
| 帧率稳定性 |
50-60 FPS |
30-45 FPS (GC 卡顿) |
✅ WebF |
| OOM 风险 |
低 |
高 |
✅ WebF |
*注意:V8 的 JIT 优势在低端设备上被严重削弱:
- JIT 编译本身消耗 CPU 和内存
- 内存压力导致更频繁的 GC
- 2GB RAM 下 WebView 可能频繁触发内存回收,造成卡顿
结论:WebF 明显优于 WebView ✅
对于 RK3566Pro 这类仅有 2GB 内存的设备,内存是关键瓶颈:
- 系统占用约 800MB-1GB
- 剩余可用约 1GB
- WebView 占用 150-300MB 后,App 只剩 700-850MB
- WebF 只占 40-80MB,App 可用 920-960MB
- 这 100-200MB 的差距在低端设备上可能决定应用是否流畅运行
中端设备 (骁龙 7 Gen 1, 6GB RAM)
| 指标 |
WebF |
WebView |
胜出 |
| 冷启动时间 |
80-150ms |
300-800ms |
✅ WebF (3-5x) |
| 内存占用 |
40-80MB |
150-300MB |
✅ WebF |
| JavaScript 执行 |
基准 |
快 10-30x |
✅ WebView |
| 帧率稳定性 |
60 FPS 稳定 |
50-60 FPS |
✅ WebF |
| 复杂动画 |
流畅 |
较流畅 |
接近 |
结论:WebF 略优,取决于应用类型
高端设备 (骁龙 8 Gen 3, 12GB RAM)
| 指标 |
WebF |
WebView |
胜出 |
| 冷启动时间 |
50-100ms |
200-500ms |
✅ WebF (2-5x) |
| 内存占用 |
40-80MB |
150-300MB |
✅ WebF |
| JavaScript 执行 |
基准 |
快 20-50x |
✅ WebView |
| 帧率稳定性 |
60 FPS |
60 FPS |
持平 |
| 复杂计算 |
较慢 |
快 |
✅ WebView |
结论:取决于应用类型
- UI 密集型应用 → WebF
- 计算密集型应用 → WebView
针对 RK3566Pro (2GB)
┌────────────────────────────────────────────────────────────────┐
│ RK3566Pro 设备特性 │
├────────────────────────────────────────────────────────────────┤
│ CPU: ARM Cortex-A55 四核 @ 1.8GHz (能效核,非性能核) │
│ GPU: Mali-G52 (中低端) │
│ RAM: 2GB (严重受限) │
│ 特点: 适合 IoT/工业/低成本消费电子 │
└────────────────────────────────────────────────────────────────┘
WebF 优势场景(推荐):
- ✅ UI 交互为主的应用(列表、表单、导航)
- ✅ 需要快速启动的应用
- ✅ 长时间运行的应用(内存稳定)
- ✅ 动画要求高的应用(Flutter 60fps)
WebView 可能更好的场景:
- 需要执行大量复杂 JavaScript 计算
- 需要完整浏览器功能(如复杂 Canvas 操作)
- 依赖 V8 特定 API 或优化
实际测试建议
在 RK3566Pro 设备上可以进行以下性能测试:
// 在 WebF 应用中添加性能监控
performance.mark('app_start');
// ... 应用初始化代码 ...
performance.mark('first_paint');
performance.measure('startup_time', 'app_start', 'first_paint');
// 获取内存使用
final memoryUsage = await window.__memory_usage__();
print('Memory: $memoryUsage');
关键指标对比:
- 冷启动到首屏时间
- 内存峰值
- 滑动列表时的帧率
- 长时间运行后的内存增长
最终总结
| 设备档次 |
推荐方案 |
原因 |
| 低端 (2GB) |
WebF ✅ |
内存是关键瓶颈,WebF 省 2-4 倍内存 |
| 中端 (6GB) |
WebF 略优 |
启动快,帧率稳定,除非 JS 计算密集 |
| 高端 (12GB) |
取决于场景 |
内存不再是问题,看 JS 计算需求 |
对于 RK3566Pro (2GB) 设备,强烈建议使用 WebF,因为:
- 内存节省 100-200MB,对仅有 2GB 的设备是巨大差异
- 启动速度快 3-6 倍,用户体验明显更好
- 帧率更稳定,不会因频繁的垃圾回收导致卡顿
- V8 的 JIT 编译优势在这种低端 CPU 上被巨大的内存压力所抵消
如果你想深入了解 Web 标准在混合开发中的应用,或与其他开发者交流跨平台技术,欢迎访问 云栈社区 的相关板块进行探讨。