Skip to content

Commit fd0174b

Browse files
committed
Added a (WIP) struct for keyed elements
1 parent 274d8c5 commit fd0174b

File tree

4 files changed

+61
-16
lines changed

4 files changed

+61
-16
lines changed

src/dom_types.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,13 @@ impl<Ms> UpdateEl<El<Ms>> for Tag {
437437
}
438438
}
439439

440+
impl<Ms> UpdateEl<El<Ms>> for Optimize {
441+
// This, or some other mechanism seems to work for String too... note sure why.
442+
fn update(self, el: &mut El<Ms>) {
443+
el.optimizations.push(self)
444+
}
445+
}
446+
440447
/// Similar to tag population.
441448
macro_rules! make_attrs {
442449
// Create shortcut macros for any element; populate these functions in this module.
@@ -1019,6 +1026,13 @@ make_tags! {
10191026
Style => "style", View => "view"
10201027
}
10211028

1029+
/// WIP that marks elements in ways to improve diffing and rendering efficiency.
1030+
#[derive(Copy, Clone, Debug)]
1031+
pub enum Optimize {
1032+
Key(u32), // Helps correctly match children, prevening unecessary rerenders
1033+
Static, // unimplemented, and possibly unecessary
1034+
}
1035+
10221036
/// An component in our virtual DOM.
10231037
#[derive(Debug)] // todo: Custom debug implementation where children are on new lines and indented.
10241038
pub struct El<Ms: 'static> {
@@ -1052,6 +1066,7 @@ pub struct El<Ms: 'static> {
10521066
// Lifecycle hooks
10531067
pub hooks: LifecycleHooks,
10541068
pub empty: bool, // Indicates not to render anything.
1069+
optimizations: Vec<Optimize>
10551070
}
10561071

10571072
type HookFn = Box<FnMut(&web_sys::Node)>;
@@ -1102,7 +1117,8 @@ impl<Ms> El<Ms> {
11021117
// static: false,
11031118
// static_to_parent: false,
11041119
hooks: LifecycleHooks::default(),
1105-
empty: false
1120+
empty: false,
1121+
optimizations: Vec::new(),
11061122
}
11071123
}
11081124

@@ -1171,6 +1187,16 @@ impl<Ms> El<Ms> {
11711187
self.text = Some(text.into())
11721188
}
11731189

1190+
/// Shortcut for finding the key, if one exists
1191+
pub fn key(&self) -> Option<u32> {
1192+
for o in &self.optimizations {
1193+
if let Optimize::Key(key) = o {
1194+
return Some(*key)
1195+
}
1196+
}
1197+
None
1198+
}
1199+
11741200
/// Output the HTML of this node, including all its children, recursively.
11751201
fn _html(&self) -> String {
11761202
let text = self.text.clone().unwrap_or_default();
@@ -1209,6 +1235,7 @@ impl<Ms> El<Ms> {
12091235
// control: self.control,
12101236
hooks: LifecycleHooks::default(),
12111237
empty: false,
1238+
optimizations: Vec::new(),
12121239
}
12131240
}
12141241

@@ -1244,6 +1271,7 @@ impl<Ms> Clone for El<Ms> {
12441271
namespace: self.namespace.clone(),
12451272
hooks: LifecycleHooks::default(),
12461273
empty: self.empty,
1274+
optimizations: self.optimizations.clone(),
12471275
}
12481276
}
12491277
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub mod prelude {
8383
pub use crate::{
8484
dom_types::{
8585
did_mount, did_update, input_ev, keyboard_ev, mouse_ev, raw_ev, simple_ev,
86-
will_unmount, At, El, Ev, Tag, UpdateEl,
86+
will_unmount, At, El, Ev, Optimize::Key, Tag, UpdateEl,
8787
},
8888
shortcuts::*, // appears not to work.
8989
// vdom::{Update, Update::Render, Update::Skip, Update::RenderThen},

src/routing.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ fn get_search() -> String {
7676
/// For setting up landing page routing. Unlike normal routing, we can't rely
7777
/// on the popstate state, so must go off path, hash, and search directly.
7878
pub fn initial<Ms, Mdl>(app: App<Ms, Mdl>, routes: fn(&Url) -> Ms) -> App<Ms, Mdl>
79-
where
80-
Ms: Clone + 'static,
81-
Mdl: 'static,
79+
where
80+
Ms: Clone + 'static,
81+
Mdl: 'static,
8282
{
8383
let raw_path = get_path();
8484
let path_ref: Vec<&str> = raw_path.split('/').collect();
@@ -211,12 +211,10 @@ pub fn setup_popstate_listener<Ms, Mdl>(app: &App<Ms, Mdl>, routes: fn(&Url) ->
211211
app.data.popstate_closure.replace(Some(closure));
212212
}
213213

214-
/// Set up a listener that intercepts clicks on <a> and <button> tags, so we can prevent page reloads for
215-
/// internal links. Run this on load.
214+
/// Set up a listener that intercepts clicks on elements containing an Href attribute,
215+
/// so we can prevent page refreshfor internal links, and route internally. Run this on load.
216216
pub fn setup_link_listener<Ms: Clone, Mdl>(app: &App<Ms, Mdl>, routes: fn(&Url) -> Ms) {
217217
// todo DRY with setup_popstate listener.
218-
// todo Deal with excessive nesting.
219-
220218
let app_for_closure = app.clone();
221219
let closure = Closure::wrap(
222220
Box::new(move |event: web_sys::Event| {
@@ -231,16 +229,17 @@ pub fn setup_link_listener<Ms: Clone, Mdl>(app: &App<Ms, Mdl>, routes: fn(&Url)
231229
if let Some(first) = href.chars().next() {
232230
// The first character being / indicates a rel link, which is what
233231
// we're intercepting.
234-
if first == '/' {
235-
event.prevent_default();
236-
// Route internally based on href's value
237-
let url = Url::new(href.split('/').collect());
238-
app_for_closure.update(routes(&url));
239-
push_route(url);
232+
// todo: Handle other cases that imply a relative link.
233+
if first != '/' {
234+
return
240235
}
236+
event.prevent_default(); // Prevent page refresh
237+
// Route internally based on href's value
238+
let url = Url::new(href.split('/').collect());
239+
app_for_closure.update(routes(&url));
240+
push_route(url);
241241
}
242242
}
243-
244243
}
245244
}
246245
})

src/vdom.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,8 +574,26 @@ fn patch<Ms: Clone>(
574574
let mut old_children_patched = Vec::new();
575575

576576
for (i_new, child_new) in new.children.iter_mut().enumerate() {
577+
578+
// If a key's specified, use it to match the child
579+
// There can be multiple optomizations, but assume one key. If there are multiple
580+
// keys, use the first (There should only be one, but no constraints atm).
581+
if let Some(key) = child_new.key() {
582+
let _matching = old.children.iter().filter(|c| c.key() == Some(key));
583+
// todo continue implementation: Patch and re-order.
584+
}
585+
586+
577587
match old.children.get(i_new) {
578588
Some(child_old) => {
589+
// todo: This approach is still inefficient use of key, since it overwrites
590+
// todo non-matching keys, preventing them from being found later.
591+
if let Some(key) = child_new.key() {
592+
if child_old.key() == Some(key) {
593+
continue
594+
}
595+
}
596+
579597
// Don't compare equality here; we do that at the top of this function
580598
// in the recursion.
581599
patch(document, &mut child_old.clone(), child_new, &old_el_ws, &mailbox);

0 commit comments

Comments
 (0)