321 lines
9.4 KiB
Markdown
321 lines
9.4 KiB
Markdown
# Dioxus Renderers Architecture
|
|
|
|
Dioxus supports multiple rendering backends through a common trait-based abstraction. Each renderer implements the same interfaces to enable cross-platform component reuse.
|
|
|
|
## Core Abstraction
|
|
|
|
### WriteMutations Trait
|
|
The bridge between VirtualDOM and real DOM implementations:
|
|
- `append_children(id, count)` - Add N nodes to element
|
|
- `assign_node_id(path, id)` - Mark element at template path
|
|
- `create_placeholder(id)` - Create marker node
|
|
- `create_text_node(value, id)` - Create text node
|
|
- `load_template(template, index, id)` - Clone from template cache
|
|
- `replace_node_with(id, count)` - Replace element
|
|
- `set_attribute(name, ns, value, id)` - Update attribute
|
|
- `create_event_listener(name, id)` - Register listener
|
|
- `remove_node(id)` - Delete element
|
|
|
|
### HtmlEventConverter Trait
|
|
Maps platform-specific events to standardized Dioxus types:
|
|
- Each renderer provides its own implementation
|
|
- Converts `PlatformEventData` (renderer-specific) to typed event data
|
|
- Enables event polymorphism across platforms
|
|
|
|
### Event Flow Pattern
|
|
```
|
|
Platform Event → Captured by renderer
|
|
→ Converted via HtmlEventConverter
|
|
→ runtime.handle_event(name, event, element_id)
|
|
→ VirtualDOM handlers invoked
|
|
→ State changes → Re-render
|
|
→ WriteMutations applied
|
|
```
|
|
|
|
## Web Renderer (dioxus-web)
|
|
|
|
### WebsysDom Structure
|
|
```
|
|
WebsysDom
|
|
├── interpreter: Sledgehammer JS interpreter
|
|
├── document: web_sys::Document reference
|
|
├── root: Root DOM node
|
|
├── templates: HashMap<Template, u16>
|
|
├── runtime: Rc<Runtime>
|
|
└── (hydration): skip_mutations, suspense_hydration_ids
|
|
```
|
|
|
|
### Mutation Implementation
|
|
- Directly delegates to JavaScript interpreter via wasm-bindgen
|
|
- Templates serialized once, stored in JS, instantiated by reference
|
|
- Sledgehammer interpreter maintains stack of nodes being constructed
|
|
- Binary protocol enables efficient mutation batching
|
|
|
|
### Event Handling
|
|
- Single delegated listener on root element
|
|
- Event.target walked up DOM tree for `data-dioxus-id` attribute
|
|
- `WebEventConverter` converts web_sys events to Dioxus types
|
|
- Supports all standard DOM events (mouse, keyboard, touch, etc.)
|
|
|
|
### Hydration System
|
|
1. SSR server renders HTML with `dio_el` data attributes
|
|
2. Client receives base64-encoded hydration context
|
|
3. VirtualDOM rebuilt with `skip_mutations = true`
|
|
4. Client traverses pre-rendered DOM assigning element IDs
|
|
5. Streaming hydration for suspense boundaries via `rehydrate_streaming()`
|
|
|
|
### Launch Flow
|
|
```
|
|
launch(root_component, contexts, config)
|
|
→ Create VirtualDom
|
|
→ Create WebsysDom wrapper
|
|
→ If hydrate: Deserialize data, rebuild with skip_mutations
|
|
→ Otherwise: vdom.rebuild(&mut websys_dom)
|
|
→ Main loop: wait_for_work() → render_immediate() → flush_edits()
|
|
```
|
|
|
|
## Desktop Renderer (dioxus-desktop)
|
|
|
|
### Wry/Tao Integration
|
|
Uses Wry webview library with Tao window management.
|
|
|
|
### App Structure
|
|
```
|
|
App
|
|
├── unmounted_dom: Cell<Option<VirtualDom>>
|
|
├── webviews: HashMap<WindowId, WebviewInstance>
|
|
├── shared: Rc<SharedContext>
|
|
│ ├── event_handlers: WindowEventHandlers
|
|
│ ├── pending_webviews: Vec<PendingWebview>
|
|
│ ├── shortcut_manager: ShortcutRegistry
|
|
│ └── websocket: EditWebsocket
|
|
└── control_flow: ControlFlow
|
|
```
|
|
|
|
### WebviewEdits
|
|
Implements WriteMutations for wry-based rendering:
|
|
- Delegates to `WryQueue` managing mutation batch
|
|
- WebSocket server on random port for mutation transmission
|
|
- Binary protocol via Sledgehammer interpreter
|
|
|
|
### IPC (Interprocess Communication)
|
|
```
|
|
Browser event → JavaScript → window.postMessage()
|
|
→ Wry intercepts request
|
|
→ Extract dioxus-data header (base64 JSON)
|
|
→ IpcMessage { method, params }
|
|
→ Handle: UserEvent, Query, BrowserOpen, Initialize
|
|
```
|
|
|
|
### Protocol Handler
|
|
- `dioxus://` custom protocol for asset serving
|
|
- Handles `__events` path for event processing
|
|
- `__file_dialog` for file selection
|
|
- Custom handler namespaces for user-provided handlers
|
|
- Checks `dioxus_asset_resolver` for bundled assets
|
|
|
|
### Native Features
|
|
- Menu integration via muda crate
|
|
- System tray via trayicon
|
|
- Global hotkeys via global_hotkey crate
|
|
- File dialogs and drag-drop support
|
|
- Headless mode for testing
|
|
|
|
### Configuration
|
|
```
|
|
Config
|
|
├── WindowBuilder customization
|
|
├── Custom event loop
|
|
├── Protocols and async protocols
|
|
├── Pre-rendered HTML template
|
|
├── Context menu disable flag
|
|
├── Background color (RGBA)
|
|
└── Devtools support toggle
|
|
```
|
|
|
|
## Native Renderer (dioxus-native)
|
|
|
|
### Blitz Integration
|
|
- Blitz layout engine for CSS layout
|
|
- Vello for GPU-accelerated vector rendering
|
|
- Winit for cross-platform window management
|
|
- Not a browser engine - custom native rendering pipeline
|
|
|
|
### DioxusNativeWindowRenderer
|
|
```
|
|
DioxusNativeWindowRenderer
|
|
├── anyrender-vello wrapper
|
|
├── WindowRenderer trait implementation
|
|
├── GPU features configurable
|
|
└── Custom paint support
|
|
```
|
|
|
|
### Rendering Pipeline
|
|
```
|
|
VirtualDOM components
|
|
→ DioxusNativeDOM
|
|
→ Blitz DOM tree with CSS
|
|
→ Blitz layout engine
|
|
→ Vello renderer
|
|
→ GPU rendering
|
|
```
|
|
|
|
### Layout and CSS
|
|
- CSS 2.1+ parser (not full CSS 3)
|
|
- Flexbox-based layout model
|
|
- Computed styles attached to element nodes
|
|
- Layout computed bottom-up during mutation
|
|
|
|
### Application Handler (Winit)
|
|
```
|
|
Event::NewEvents(StartCause::Init)
|
|
→ Create initial window
|
|
→ DioxusDocument with VirtualDOM
|
|
|
|
Resumed → Renderer.resume()
|
|
|
|
WindowEvent::RedrawRequested
|
|
→ VirtualDOM.render_immediate()
|
|
→ Mutations applied to Blitz DOM
|
|
→ Layout computed
|
|
→ Render frame
|
|
|
|
WindowEvent::Resized → Queue redraw
|
|
```
|
|
|
|
## LiveView Renderer (dioxus-liveview)
|
|
|
|
### Server-Rendered Architecture
|
|
```
|
|
Client (Browser with WebSocket)
|
|
↓ events
|
|
Server (VirtualDOM runs here)
|
|
↓ mutations (binary protocol)
|
|
Client receives mutations → Sledgehammer applies
|
|
```
|
|
|
|
### LiveViewPool
|
|
- Thread pool using `tokio_util::task::LocalPoolHandle`
|
|
- Each client spawns pinned task
|
|
- VirtualDOM runs on task's executor
|
|
- Pool handles multiple concurrent clients
|
|
|
|
### Per-Client Lifecycle
|
|
```
|
|
LiveViewPool::launch_virtualdom(ws, || VirtualDom::new())
|
|
→ Create MutationState, QueryEngine
|
|
→ vdom.rebuild() → Send initial HTML
|
|
→ Loop:
|
|
tokio::select! {
|
|
ws.next() → Handle event/query
|
|
vdom.wait_for_work() → Has work
|
|
query_rx.recv() → JS query
|
|
hot_reload_rx.recv() → Hot reload
|
|
}
|
|
render_immediate() → Send mutations
|
|
```
|
|
|
|
### Binary Protocol
|
|
```
|
|
MutationState::write_memory_into(&mut bytes)
|
|
→ Sledgehammer binary encoding
|
|
→ WebSocket transmission
|
|
→ Client: window.interpreter.handleEdits(bytes)
|
|
```
|
|
|
|
### Message Types
|
|
1. **Binary Frames** (mutations)
|
|
2. **Text Frames** (queries/metadata)
|
|
3. **Incoming Events** (user interactions)
|
|
|
|
### Mounted Element Queries
|
|
```
|
|
LiveviewElement methods:
|
|
scroll_offset() → JS: getScrollLeft/Top()
|
|
scroll_size() → JS: getScrollWidth/Height()
|
|
client_rect() → JS: getBoundingClientRect()
|
|
scroll_to(options) → JS: element.scrollTo()
|
|
```
|
|
|
|
## Interpreter Package (dioxus-interpreter-js)
|
|
|
|
### Sledgehammer Framework
|
|
Ultra-compact binary protocol for DOM mutations shared across renderers.
|
|
|
|
### Core Components
|
|
1. **INTERPRETER_JS** - Base interpreter class
|
|
2. **NATIVE_JS** - Platform-specific extensions
|
|
3. **SLEDGEHAMMER_JS** - Generated bindings from Rust
|
|
|
|
### MutationState
|
|
- Implements WriteMutations for binary serialization
|
|
- Channel-based mutation recording
|
|
- Template caching and deduplication
|
|
|
|
### Stack Machine Operations
|
|
```
|
|
create_text_node(value, id) → Push text node
|
|
create_placeholder(id) → Push comment node
|
|
append_children(parent_id, count) → Pop and append
|
|
replace_with(id, count) → Replace element
|
|
set_attribute(id, name, value, ns) → Set DOM attribute
|
|
new_event_listener(name, id, bubbles) → Register listener
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### 1. WriteMutations Implementation
|
|
Every renderer implements the trait differently:
|
|
- **Web**: Delegates to Sledgehammer JS
|
|
- **Desktop**: Accumulates in MutationState, sends via WebSocket
|
|
- **Native**: Applies directly to Blitz DOM
|
|
- **Liveview**: Accumulates and sends via WebSocket
|
|
|
|
### 2. Configuration Pattern
|
|
Renderers use `Box<dyn Any>` for extensible config:
|
|
```rust
|
|
launch(app, configs: Vec<Box<dyn Any>>)
|
|
→ For each config: try downcast to expected type
|
|
```
|
|
|
|
### 3. Lazy Initialization
|
|
Most renderers delay context setup until first window/request.
|
|
|
|
### 4. Event Loop Integration
|
|
- **Web**: WASM bindgen + browser event loop
|
|
- **Desktop**: Tao event loop with UserWindowEvent
|
|
- **Native**: Winit ApplicationHandler
|
|
- **Liveview**: Tokio select! macro
|
|
|
|
### 5. Mutation Batching
|
|
All non-web renderers batch mutations for efficiency:
|
|
- Accumulate in temporary structure
|
|
- Periodically flush to transport
|
|
|
|
## Adding a New Renderer
|
|
|
|
1. **Implement WriteMutations**
|
|
- Define how platform applies DOM-like mutations
|
|
- Handle template system
|
|
- Manage stack machine state
|
|
|
|
2. **Implement HtmlEventConverter**
|
|
- Map platform events to Dioxus types
|
|
- Handle serialization if needed
|
|
|
|
3. **Create Launch Function**
|
|
- Create VirtualDOM
|
|
- Initialize renderer structures
|
|
- Enter platform event loop
|
|
- Call `vdom.render_immediate(&mut mutations)`
|
|
|
|
4. **Define Configuration**
|
|
- Create Config struct
|
|
- Support `Box<dyn Any>` downcasting
|
|
|
|
5. **Optional Enhancements**
|
|
- Custom mount event data
|
|
- Document integration
|
|
- History support
|
|
- Asset serving
|