xilem_web: Fix `DomChildrenSplice::with_scratch` (#484)

I'm surprised that this was not noticed yet,
`DomChildrenSplice::with_scratch` previously only appended new elements
to the end of the children of an element in the DOM tree, whereas it
should've inserted it at the current index of the `ElementSplice`. This
should fix this, using a `DocumentFragment` which is shared on the
`ViewCtx` to avoid unnecessary allocations.
This commit is contained in:
Philipp Mildenberger 2024-08-05 11:08:34 +02:00 committed by GitHub
parent 0bca54ae52
commit 544a4a1ca9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 30 additions and 6 deletions

View File

@ -31,6 +31,7 @@ features = [
"console", "console",
"CssStyleDeclaration", "CssStyleDeclaration",
"Document", "Document",
"DocumentFragment",
"DomTokenList", "DomTokenList",
"Element", "Element",
"Event", "Event",

View File

@ -25,10 +25,20 @@ impl MessageThunk {
} }
/// The [`View`](`crate::core::View`) `Context` which is used for all [`DomView`](`crate::DomView`)s /// The [`View`](`crate::core::View`) `Context` which is used for all [`DomView`](`crate::DomView`)s
#[derive(Default)]
pub struct ViewCtx { pub struct ViewCtx {
id_path: Vec<ViewId>, id_path: Vec<ViewId>,
app_ref: Option<Box<dyn AppRunner>>, app_ref: Option<Box<dyn AppRunner>>,
pub(crate) fragment: Rc<web_sys::DocumentFragment>,
}
impl Default for ViewCtx {
fn default() -> Self {
ViewCtx {
id_path: Vec::default(),
app_ref: None,
fragment: Rc::new(crate::document().create_document_fragment()),
}
}
} }
impl ViewCtx { impl ViewCtx {

View File

@ -3,8 +3,8 @@
//! Basic builder functions to create DOM elements, such as [`html::div`] //! Basic builder functions to create DOM elements, such as [`html::div`]
use std::any::Any;
use std::borrow::Cow; use std::borrow::Cow;
use std::{any::Any, rc::Rc};
use wasm_bindgen::{JsCast, UnwrapThrowExt}; use wasm_bindgen::{JsCast, UnwrapThrowExt};
use crate::{ use crate::{
@ -135,6 +135,7 @@ pub struct DomChildrenSplice<'a, 'b, 'c, 'd> {
children: VecSplice<'b, 'c, AnyPod>, children: VecSplice<'b, 'c, AnyPod>,
ix: usize, ix: usize,
parent: &'d web_sys::Node, parent: &'d web_sys::Node,
fragment: Rc<web_sys::DocumentFragment>,
parent_was_removed: bool, parent_was_removed: bool,
} }
@ -144,6 +145,7 @@ impl<'a, 'b, 'c, 'd> DomChildrenSplice<'a, 'b, 'c, 'd> {
children: &'b mut Vec<AnyPod>, children: &'b mut Vec<AnyPod>,
vec_splice_scratch: &'c mut Vec<AnyPod>, vec_splice_scratch: &'c mut Vec<AnyPod>,
parent: &'d web_sys::Node, parent: &'d web_sys::Node,
fragment: Rc<web_sys::DocumentFragment>,
parent_was_deleted: bool, parent_was_deleted: bool,
) -> Self { ) -> Self {
Self { Self {
@ -151,6 +153,7 @@ impl<'a, 'b, 'c, 'd> DomChildrenSplice<'a, 'b, 'c, 'd> {
children: VecSplice::new(children, vec_splice_scratch), children: VecSplice::new(children, vec_splice_scratch),
ix: 0, ix: 0,
parent, parent,
fragment,
parent_was_removed: parent_was_deleted, parent_was_removed: parent_was_deleted,
} }
} }
@ -159,12 +162,20 @@ impl<'a, 'b, 'c, 'd> DomChildrenSplice<'a, 'b, 'c, 'd> {
impl<'a, 'b, 'c, 'd> ElementSplice<AnyPod> for DomChildrenSplice<'a, 'b, 'c, 'd> { impl<'a, 'b, 'c, 'd> ElementSplice<AnyPod> for DomChildrenSplice<'a, 'b, 'c, 'd> {
fn with_scratch<R>(&mut self, f: impl FnOnce(&mut AppendVec<AnyPod>) -> R) -> R { fn with_scratch<R>(&mut self, f: impl FnOnce(&mut AppendVec<AnyPod>) -> R) -> R {
let ret = f(self.scratch); let ret = f(self.scratch);
for element in self.scratch.drain() { if !self.scratch.is_empty() {
for element in self.scratch.drain() {
self.fragment
.append_child(element.node.as_ref())
.unwrap_throw();
self.children.insert(element);
self.ix += 1;
}
self.parent self.parent
.append_child(element.node.as_ref()) .insert_before(
self.fragment.as_ref(),
self.children.next_mut().map(|p| p.node.as_ref()),
)
.unwrap_throw(); .unwrap_throw();
self.children.insert(element);
self.ix += 1;
} }
ret ret
} }
@ -262,6 +273,7 @@ where
&mut element.props.children, &mut element.props.children,
&mut state.vec_splice_scratch, &mut state.vec_splice_scratch,
element.node.as_ref(), element.node.as_ref(),
ctx.fragment.clone(),
element.was_removed, element.was_removed,
); );
children.dyn_seq_rebuild( children.dyn_seq_rebuild(
@ -290,6 +302,7 @@ pub(crate) fn teardown_element<State, Action, Element, SeqMarker>(
&mut element.props.children, &mut element.props.children,
&mut state.vec_splice_scratch, &mut state.vec_splice_scratch,
element.node.as_ref(), element.node.as_ref(),
ctx.fragment.clone(),
true, true,
); );
children.dyn_seq_teardown(&mut state.seq_state, ctx, &mut dom_children_splice); children.dyn_seq_teardown(&mut state.seq_state, ctx, &mut dom_children_splice);