optimizations for wasm size (#582)

* optimize for size

* fix tests

* revert log feature

* make backtrace not optional

* remove dev feature from web dev-deps
This commit is contained in:
Demonthos 2022-11-17 00:22:13 -06:00 committed by GitHub
parent 8ea61e1b3e
commit f21c8423eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 61 additions and 28 deletions

View file

@ -23,9 +23,6 @@ rustc-hash = "1.1.0"
# Used in diffing
longest-increasing-subsequence = "0.1.0"
# internall used
log = { version = "0.4" }
futures-util = { version = "0.3", default-features = false }
smallvec = "1.6"
@ -34,16 +31,18 @@ slab = "0.4"
futures-channel = "0.3.21"
# internally used
log = "0.4"
# used for noderefs
once_cell = "1.8"
indexmap = "1.7"
# Serialize the Edits for use in Webview/Liveview instances
serde = { version = "1", features = ["derive"], optional = true }
# todo: I want to get rid of this
backtrace = "0.3"
backtrace = { version = "0.3" }
# allows cloing trait objects
dyn-clone = "1.0.9"
@ -52,4 +51,4 @@ dyn-clone = "1.0.9"
default = []
serialize = ["serde"]
debug_vdom = []
hot-reload = []
hot-reload = []

View file

@ -2,13 +2,13 @@
//!
//! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
use std::{collections::VecDeque, iter::FromIterator, task::Poll};
use crate::diff::DiffState;
use crate::innerlude::*;
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
use futures_util::{future::poll_fn, StreamExt};
use indexmap::IndexSet;
use rustc_hash::FxHashSet;
use std::{collections::VecDeque, iter::FromIterator, task::Poll};
/// A virtual node system that progresses user events and diffs UI trees.
///
@ -107,7 +107,8 @@ pub struct VirtualDom {
scopes: ScopeArena,
pending_messages: VecDeque<SchedulerMsg>,
dirty_scopes: IndexSet<ScopeId>,
dirty_scopes: Vec<ScopeId>,
removed_scopes: FxHashSet<ScopeId>,
channel: (
UnboundedSender<SchedulerMsg>,
@ -238,8 +239,9 @@ impl VirtualDom {
root: ElementId(0),
scopes,
channel,
dirty_scopes: IndexSet::from_iter([ScopeId(0)]),
dirty_scopes: Vec::from_iter([ScopeId(0)]),
pending_messages: VecDeque::new(),
removed_scopes: FxHashSet::default(),
}
}
@ -396,11 +398,18 @@ impl VirtualDom {
}
}
SchedulerMsg::Immediate(s) => {
self.dirty_scopes.insert(s);
self.mark_dirty_scope(s);
}
SchedulerMsg::DirtyAll => {
for id in self.scopes.scopes.borrow().keys() {
self.dirty_scopes.insert(*id);
let dirty = self
.scopes
.scopes
.borrow()
.keys()
.copied()
.collect::<Vec<_>>();
for id in dirty {
self.mark_dirty_scope(id);
}
}
#[cfg(any(feature = "hot-reload", debug_assertions))]
@ -474,6 +483,7 @@ impl VirtualDom {
pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> {
let mut committed_mutations = vec![];
self.scopes.template_bump.reset();
self.removed_scopes.clear();
while !self.dirty_scopes.is_empty() {
let scopes = &self.scopes;
@ -492,7 +502,10 @@ impl VirtualDom {
});
if let Some(scopeid) = self.dirty_scopes.pop() {
if !ran_scopes.contains(&scopeid) {
if scopes.get_scope(scopeid).is_some()
&& !self.removed_scopes.contains(&scopeid)
&& !ran_scopes.contains(&scopeid)
{
ran_scopes.insert(scopeid);
self.scopes.run_scope(scopeid);
@ -501,9 +514,8 @@ impl VirtualDom {
let DiffState { mutations, .. } = diff_state;
for scope in &mutations.dirty_scopes {
self.dirty_scopes.remove(scope);
}
self.removed_scopes
.extend(mutations.dirty_scopes.iter().copied());
if !mutations.edits.is_empty() {
committed_mutations.push(mutations);
@ -729,6 +741,23 @@ impl VirtualDom {
})
.unwrap()
}
fn mark_dirty_scope(&mut self, scope_id: ScopeId) {
let scopes = &self.scopes;
if let Some(scope) = scopes.get_scope(scope_id) {
let height = scope.height;
let id = scope_id.0;
if let Err(index) = self.dirty_scopes.binary_search_by(|new| {
let scope = scopes.get_scope(*new).unwrap();
let new_height = scope.height;
let new_id = &scope.scope_id();
height.cmp(&new_height).then(new_id.0.cmp(&id))
}) {
self.dirty_scopes.insert(index, scope_id);
log::info!("mark_dirty_scope: {:?}", self.dirty_scopes);
}
}
}
}
/*
@ -736,7 +765,8 @@ Scopes and ScopeArenas are never dropped internally.
An app will always occupy as much memory as its biggest form.
This means we need to handle all specifics of drop *here*. It's easier
to reason about centralizing all the drop logic in one spot rather than scattered in each module.
to reason about centralizing all the drop
logic in one spot rather than scattered in each module.
Broadly speaking, we want to use the remove_nodes method to clean up *everything*
This will drop listeners, borrowed props, and hooks for all components.

View file

@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
[dependencies]
dioxus-core = { path = "../../packages/core", version = "^0.2.1" }
futures-channel = "0.3.21"
log = { version = "0.4" }
log = "0.4"
[dev-dependencies]

View file

@ -20,7 +20,7 @@ dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1", features
js-sys = "0.3.56"
wasm-bindgen = { version = "0.2.79", features = ["enable-interning"] }
wasm-bindgen-futures = "0.4.29"
log = { version = "0.4.14" }
log = "0.4.14"
rustc-hash = "1.1.0"
console_error_panic_hook = { version = "0.1.7", optional = true }
once_cell = "1.9.0"
@ -28,7 +28,6 @@ anyhow = "1.0.53"
gloo-timers = { version = "0.2.3", features = ["futures"] }
futures-util = "0.3.19"
smallstr = "0.2.0"
serde-wasm-bindgen = "0.4.2"
futures-channel = "0.3.21"
serde_json = { version = "1.0" }
@ -78,13 +77,14 @@ features = [
]
[features]
default = ["panic_hook"]
default = ["panic_hook", "hydrate"]
panic_hook = ["console_error_panic_hook"]
hot-reload = ["dioxus/hot-reload"]
hydrate = []
[dev-dependencies]
dioxus = { path = "../dioxus" }
wasm-bindgen-test = "0.3.29"
dioxus-ssr = { path = "../ssr" }
wasm-logger = "0.2.0"
dioxus-web = { path = "." }

View file

@ -12,7 +12,7 @@ use dioxus_html::event_bubbles;
use dioxus_interpreter_js::Interpreter;
use js_sys::Function;
use std::{any::Any, rc::Rc, sync::Arc};
use wasm_bindgen::{closure::Closure, JsCast};
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
use web_sys::{Document, Element, Event, HtmlElement};
use crate::Config;
@ -148,11 +148,11 @@ impl WebsysDom {
}
DomEdit::CreateTextNode { text, root } => {
let text = serde_wasm_bindgen::to_value(text).unwrap();
let text = JsValue::from_str(text);
self.interpreter.CreateTextNode(text, root)
}
DomEdit::SetText { root, text } => {
let text = serde_wasm_bindgen::to_value(text).unwrap();
let text = JsValue::from_str(text);
self.interpreter.SetText(root, text)
}
DomEdit::SetAttribute {
@ -161,7 +161,7 @@ impl WebsysDom {
value,
ns,
} => {
let value = serde_wasm_bindgen::to_value(&value).unwrap();
let value = JsValue::from_str(&value.to_string());
self.interpreter.SetAttribute(root, field, value, ns)
}
DomEdit::CloneNode { id, new_id } => self.interpreter.CloneNode(id, new_id),

View file

@ -67,6 +67,7 @@ mod cfg;
mod dom;
#[cfg(any(feature = "hot-reload", debug_assertions))]
mod hot_reload;
#[cfg(feature = "hydrate")]
mod rehydrate;
// mod ric_raf;
mod util;
@ -164,7 +165,8 @@ where
pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T, cfg: Config) {
let mut dom = VirtualDom::new_with_props(root, root_props);
if cfg!(feature = "panic_hook") && cfg.default_panic_hook {
#[cfg(feature = "panic_hook")]
if cfg.default_panic_hook {
console_error_panic_hook::set_once();
}
@ -194,6 +196,8 @@ pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T
// it's a waste to produce edits just to get the vdom loaded
let _ = dom.rebuild();
#[cfg(feature = "hydrate")]
#[allow(unused_variables)]
if let Err(err) = websys_dom.rehydrate(&dom) {
log::error!(
"Rehydration failed {:?}. Rebuild DOM into element from scratch",