123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- use std::ops::Deref;
- use crate::{
- any_props::AnyProps,
- innerlude::{DirtyScope, ElementRef, MountId, VComponent, WriteMutations},
- nodes::RenderReturn,
- nodes::VNode,
- scopes::ScopeId,
- virtual_dom::VirtualDom,
- };
- impl VirtualDom {
- pub(crate) fn diff_scope(
- &mut self,
- to: &mut impl WriteMutations,
- scope: ScopeId,
- new_nodes: RenderReturn,
- ) {
- self.runtime.scope_stack.borrow_mut().push(scope);
- let scope_state = &mut self.scopes[scope.0];
- // Load the old and new bump arenas
- let new = &new_nodes;
- let old = scope_state.last_rendered_node.take().unwrap();
- old.diff_node(new, self, to);
- let scope_state = &mut self.scopes[scope.0];
- scope_state.last_rendered_node = Some(new_nodes);
- self.runtime.scope_stack.borrow_mut().pop();
- }
- /// Create a new template [`VNode`] and write it to the [`Mutations`] buffer.
- ///
- /// This method pushes the ScopeID to the internal scopestack and returns the number of nodes created.
- pub(crate) fn create_scope(
- &mut self,
- to: &mut impl WriteMutations,
- scope: ScopeId,
- new_node: RenderReturn,
- parent: Option<ElementRef>,
- ) -> usize {
- self.runtime.scope_stack.borrow_mut().push(scope);
- // Create the node
- let nodes = new_node.create(self, to, parent);
- // Then set the new node as the last rendered node
- self.scopes[scope.0].last_rendered_node = Some(new_node);
- self.runtime.scope_stack.borrow_mut().pop();
- nodes
- }
- }
- impl VNode {
- pub(crate) fn diff_vcomponent(
- &self,
- mount: MountId,
- idx: usize,
- new: &VComponent,
- old: &VComponent,
- scope_id: ScopeId,
- parent: Option<ElementRef>,
- dom: &mut VirtualDom,
- to: &mut impl WriteMutations,
- ) {
- println!(
- "diffing vcomponent: {new:#?} vs {old:#?}",
- new = new,
- old = old
- );
- // Replace components that have different render fns
- if old.render_fn != new.render_fn {
- println!("render fns are different, replacing");
- return self.replace_vcomponent(mount, idx, new, parent, dom, to);
- }
- println!("render fns are the same, continuing");
- // copy out the box for both
- let old_scope = &mut dom.scopes[scope_id.0];
- let old_props: &dyn AnyProps = old_scope.props.deref();
- let new_props: &dyn AnyProps = new.props.deref();
- // If the props are static, then we try to memoize by setting the new with the old
- // The target ScopeState still has the reference to the old props, so there's no need to update anything
- // This also implicitly drops the new props since they're not used
- if old_props.memoize(new_props.props()) {
- tracing::trace!(
- "Memoized props for component {:#?} ({})",
- scope_id,
- old_scope.context().name
- );
- return;
- }
- // First, move over the props from the old to the new, dropping old props in the process
- dom.scopes[scope_id.0].props = new.props.clone();
- // Now run the component and diff it
- let new = dom.run_scope(scope_id);
- dom.diff_scope(to, scope_id, new);
- let height = dom.runtime.get_context(scope_id).unwrap().height;
- dom.dirty_scopes.remove(&DirtyScope {
- height,
- id: scope_id,
- });
- }
- fn replace_vcomponent(
- &self,
- mount: MountId,
- idx: usize,
- new: &VComponent,
- parent: Option<ElementRef>,
- dom: &mut VirtualDom,
- to: &mut impl WriteMutations,
- ) {
- let scope = ScopeId(dom.mounts[mount.0].mounted_dynamic_nodes[idx]);
- let m = self.create_component_node(mount, idx, new, parent, dom, to);
- // Instead of *just* removing it, we can use the replace mutation
- dom.remove_component_node(to, scope, Some(m), true);
- }
- pub(super) fn create_component_node(
- &self,
- mount: MountId,
- idx: usize,
- component: &VComponent,
- parent: Option<ElementRef>,
- dom: &mut VirtualDom,
- to: &mut impl WriteMutations,
- ) -> usize {
- // Load up a ScopeId for this vcomponent. If it's already mounted, then we can just use that
- let scope = dom
- .new_scope(component.props.clone(), component.name)
- .context()
- .id;
- // Store the scope id for the next render
- dom.mounts[mount.0].mounted_dynamic_nodes[idx] = scope.0;
- let new = dom.run_scope(scope);
- dom.create_scope(to, scope, new, parent)
- }
- }
|