Switch to a pool of dynamic values for hot reloading (#2705)
* create the dynamic value pool * assign ids to dynamic formatted segments * separate the rendering and literal pools * rsx output compiles again * more examples compiling with new rsx expansion * update template body explanation * all workspace examples compile * fix formatted segments in keys * start hot reload diffing * fix component literal hot reloading * start integrate new hot reloading with the CLI * simple hot reloads working * Fix hot reloading blocks with components * implement hot reloading for if chains * Fix hot reloading after a template requires a full rebuild * Fix hot reloading any attribute values * remove unsafe from hot reload utils * Fix hot reloading empty rsx * add more hot reloading tests * reorganize hot reload module * fix web hydration * fix empty rsx nodes in autoformatting * fix tests * remove path sorting logic from core * make template names more consistent in debug mode * fix quote_as_hot_reload_literal for explicitly typed literals * fix can_be_shorthand for string literals * fix formatted single dynamic expression * Fix usize component properties and playwright tests * remove default implementation for TemplateBody * add a bunch more comments for diffing, scoring and why this scoring system is optimal
This commit is contained in:
1 parent
f3ca1484a1
commit
34bdcd15cf
40 files changed
+2288
-1897
No files matched your search
@@ -18,7 +18,7 @@ use crate::{
|
||||
};
|
||||
use crate::{Task, VComponent};
|
||||
use futures_util::StreamExt;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
use slab::Slab;
|
||||
use std::collections::BTreeSet;
|
||||
use std::{any::Any, rc::Rc};
|
||||
@@ -208,8 +208,8 @@ pub struct VirtualDom {
|
||||
|
||||
pub(crate) dirty_scopes: BTreeSet<ScopeOrder>,
|
||||
|
||||
// A map of overridden templates?
|
||||
pub(crate) templates: FxHashMap<TemplateId, Template>,
|
||||
// A map of templates we have sent to the renderer
|
||||
pub(crate) templates: FxHashSet<TemplateId>,
|
||||
|
||||
// Templates changes that are queued for the next render
|
||||
pub(crate) queued_templates: Vec<Template>,
|
||||
@@ -572,60 +572,6 @@ impl VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace a template at runtime. This will re-render all components that use this template.
|
||||
/// This is the primitive that enables hot-reloading.
|
||||
///
|
||||
/// The caller must ensure that the template references the same dynamic attributes and nodes as the original template.
|
||||
///
|
||||
/// This will only replace the parent template, not any nested templates.
|
||||
#[instrument(skip(self), level = "trace", name = "VirtualDom::replace_template")]
|
||||
pub fn replace_template(&mut self, template: Template) {
|
||||
// we only replace templates if hot reloading is enabled
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
// Save the template ID
|
||||
self.templates.insert(template.name, template);
|
||||
|
||||
// Only queue the template to be written if its not completely dynamic
|
||||
if !template.is_completely_dynamic() {
|
||||
self.queued_templates.push(template);
|
||||
}
|
||||
|
||||
// iterating a slab is very inefficient, but this is a rare operation that will only happen during development so it's fine
|
||||
let mut dirty = Vec::new();
|
||||
for (id, scope) in self.scopes.iter() {
|
||||
// Recurse into the dynamic nodes of the existing mounted node to see if the template is alive in the tree
|
||||
fn check_node_for_templates(node: &crate::VNode, template: Template) -> bool {
|
||||
if node.template.get().name == template.name {
|
||||
return true;
|
||||
}
|
||||
|
||||
for dynamic in node.dynamic_nodes.iter() {
|
||||
if let crate::DynamicNode::Fragment(nodes) = dynamic {
|
||||
for node in nodes {
|
||||
if check_node_for_templates(node, template) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
if let Some(sync) = scope.try_root_node() {
|
||||
if check_node_for_templates(sync, template) {
|
||||
dirty.push(ScopeId(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for dirty in dirty {
|
||||
self.mark_dirty(dirty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rebuild the virtualdom without handling any of the mutations
|
||||
///
|
||||
/// This is useful for testing purposes and in cases where you render the output of the virtualdom without
|
||||
@@ -885,11 +831,11 @@ impl VirtualDom {
|
||||
return;
|
||||
};
|
||||
let el_ref = &mount.node;
|
||||
let node_template = el_ref.template.get();
|
||||
let node_template = el_ref.template;
|
||||
let target_path = path.path;
|
||||
|
||||
// Accumulate listeners into the listener list bottom to top
|
||||
for (idx, this_path) in node_template.breadth_first_attribute_paths() {
|
||||
for (idx, this_path) in node_template.attr_paths.iter().enumerate() {
|
||||
let attrs = &*el_ref.dynamic_attrs[idx];
|
||||
|
||||
for attr in attrs.iter() {
|
||||
@@ -943,10 +889,10 @@ impl VirtualDom {
|
||||
return;
|
||||
};
|
||||
let el_ref = &mount.node;
|
||||
let node_template = el_ref.template.get();
|
||||
let node_template = el_ref.template;
|
||||
let target_path = node.path;
|
||||
|
||||
for (idx, this_path) in node_template.breadth_first_attribute_paths() {
|
||||
for (idx, this_path) in node_template.attr_paths.iter().enumerate() {
|
||||
let attrs = &*el_ref.dynamic_attrs[idx];
|
||||
|
||||
for attr in attrs.iter() {
|
||||
|
||||
Reference in new issue
Block a user