♻️ (core): Evita doble clonado de InRegion en Child
Los prototipos registrados con `InRegion` se clonaban dos veces por petición: una al ensamblar la región (`ComponentGlobal::as_child`) y otra al renderizar (`Child::render`). Ahora sólo se clonan al renderizar.
This commit is contained in:
parent
ce6b4d2581
commit
845d91d8c1
2 changed files with 33 additions and 40 deletions
|
|
@ -32,6 +32,13 @@ impl Child {
|
|||
Child(Some(Arc::new(component)))
|
||||
}
|
||||
|
||||
// Envuelve un `Arc` ya construido, sin clonar el componente. La usa `core::theme::regions` para
|
||||
// registrar los prototipos de `InRegion` sin clonar su estado hasta que `render()` obtenga la
|
||||
// copia propia que necesita mutar.
|
||||
pub(crate) fn from_arc(component: Arc<dyn Component>) -> Self {
|
||||
Child(Some(component))
|
||||
}
|
||||
|
||||
// **< Child BUILDER >**************************************************************************
|
||||
|
||||
/// Establece un componente nuevo, o lo vacía.
|
||||
|
|
@ -73,7 +80,7 @@ impl Child {
|
|||
}
|
||||
}
|
||||
|
||||
impl<C: Component + 'static> From<Embed<C>> for Child {
|
||||
impl<C: Component> From<Embed<C>> for Child {
|
||||
/// Convierte un [`Embed<C>`] en un [`Child`], consumiendo el componente tipado.
|
||||
///
|
||||
/// Útil cuando se tiene un [`Embed`] para añadir a una lista [`Children`]:
|
||||
|
|
@ -93,7 +100,7 @@ impl<C: Component + 'static> From<Embed<C>> for Child {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Component + 'static> From<T> for Child {
|
||||
impl<T: Component> From<T> for Child {
|
||||
/// Convierte cualquier componente en un [`Child`], equivalente a [`Child::with()`].
|
||||
#[inline]
|
||||
fn from(component: T) -> Self {
|
||||
|
|
@ -101,7 +108,7 @@ impl<T: Component + 'static> From<T> for Child {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Component + 'static> From<T> for ChildOp {
|
||||
impl<T: Component> From<T> for ChildOp {
|
||||
/// Convierte un componente en [`ChildOp::Add`], permitiendo pasar componentes directamente a
|
||||
/// métodos como [`Children::with_child`] sin envolverlos explícitamente.
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -7,26 +7,13 @@ use parking_lot::RwLock;
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
// Permite almacenar un componente como prototipo en regiones globales.
|
||||
// Mapea cada nombre de región con su lista de prototipos de componentes.
|
||||
//
|
||||
// Se implementa automáticamente para todo tipo que implemente [`Component`] y [`Clone`]. En cada
|
||||
// llamada a [`as_child`](Self::as_child) produce un clon fresco del estado original, de modo que
|
||||
// cada página renderiza el componente desde su estado inicial sin acumular mutaciones de peticiones
|
||||
// anteriores.
|
||||
trait ComponentGlobal: Send + Sync {
|
||||
// Devuelve un nuevo [`Child`] con una copia independiente del componente original.
|
||||
fn as_child(&self) -> Child;
|
||||
}
|
||||
|
||||
impl<T: Component + Clone + 'static> ComponentGlobal for T {
|
||||
#[inline]
|
||||
fn as_child(&self) -> Child {
|
||||
Child::with(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// Mapa de nombre de región a lista de prototipos de componentes.
|
||||
type RegionComponents = HashMap<String, Vec<Arc<dyn ComponentGlobal>>>;
|
||||
// Se comparten como `Arc<dyn Component>`. El prototipo no se clona al registrarse ni al ensamblar
|
||||
// la región (sólo se clona el `Arc`, barato). En cambio, sí se realiza un clonado del componente en
|
||||
// `Child::render()`, cuando cada petición necesita su propia copia mutable para pasar por `setup()`
|
||||
// desde un estado inicial limpio.
|
||||
type RegionComponents = HashMap<String, Vec<Arc<dyn Component>>>;
|
||||
|
||||
// Regiones globales con prototipos asociados a un tema específico.
|
||||
static THEME_REGIONS: LazyLock<RwLock<HashMap<UniqueId, RegionComponents>>> =
|
||||
|
|
@ -64,23 +51,22 @@ impl ChildrenInRegions {
|
|||
///
|
||||
/// Se recogen desde tres fuentes disponibles, en el siguiente orden:
|
||||
///
|
||||
/// 1. Prototipos globales comunes, disponibles en cualquier tema. Se clonan en cada petición
|
||||
/// 1. Prototipos globales comunes, disponibles en cualquier tema. Se comparten como `Arc` (sin
|
||||
/// clonar el componente); `Child::render()` obtiene su propia copia mutable más adelante,
|
||||
/// para que `setup()` parta siempre de un estado inicial limpio.
|
||||
/// 2. Componentes propios de la página, registrados para esta petición concreta. Se mueven en
|
||||
/// lugar de clonarse, ya que son de un único uso.
|
||||
/// 3. Prototipos del tema activo, exclusivos del tema en curso. También se clonan para asegurar
|
||||
/// que llegan a `setup()` con el mismo estado inicial.
|
||||
/// 3. Prototipos del tema activo, exclusivos del tema en curso. Se comparten igual que los
|
||||
/// comunes.
|
||||
pub fn assemble_region(&mut self, theme: ThemeRef, region: RegionRef) -> Children {
|
||||
let region_name = region.name();
|
||||
let common = COMMON_REGIONS.read();
|
||||
let themed = THEME_REGIONS.read();
|
||||
|
||||
let mut result = Children::new();
|
||||
|
||||
let region_name = region.name();
|
||||
|
||||
// 1. Prototipos globales comunes.
|
||||
if let Some(protos) = common.get(region_name) {
|
||||
for proto in protos {
|
||||
result.add(proto.as_child());
|
||||
if let Some(global_protos) = COMMON_REGIONS.read().get(region_name) {
|
||||
for proto in global_protos {
|
||||
result.add(Child::from_arc(Arc::clone(proto)));
|
||||
}
|
||||
}
|
||||
// 2. Componentes propios de la página: se mueven, no se clonan.
|
||||
|
|
@ -90,11 +76,11 @@ impl ChildrenInRegions {
|
|||
}
|
||||
}
|
||||
// 3. Prototipos del tema activo.
|
||||
if let Some(theme_map) = themed.get(&theme.type_id()) {
|
||||
if let Some(protos) = theme_map.get(region_name) {
|
||||
for proto in protos {
|
||||
result.add(proto.as_child());
|
||||
}
|
||||
if let Some(theme_region) = THEME_REGIONS.read().get(&theme.type_id())
|
||||
&& let Some(theme_protos) = theme_region.get(region_name)
|
||||
{
|
||||
for proto in theme_protos {
|
||||
result.add(Child::from_arc(Arc::clone(proto)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -168,8 +154,8 @@ impl InRegion {
|
|||
/// html! { "Aviso legal" }
|
||||
/// }));
|
||||
/// ```
|
||||
pub fn add(&self, component: impl Component + Clone + 'static) -> &Self {
|
||||
let proto: Arc<dyn ComponentGlobal> = Arc::new(component);
|
||||
pub fn add(&self, component: impl Component) -> &Self {
|
||||
let proto: Arc<dyn Component> = Arc::new(component);
|
||||
match self {
|
||||
InRegion::Content => Self::add_to_common(&CoreRegion::Content, proto),
|
||||
InRegion::Global(region) => Self::add_to_common(*region, proto),
|
||||
|
|
@ -187,7 +173,7 @@ impl InRegion {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn add_to_common(region: RegionRef, proto: Arc<dyn ComponentGlobal>) {
|
||||
fn add_to_common(region: RegionRef, proto: Arc<dyn Component>) {
|
||||
COMMON_REGIONS
|
||||
.write()
|
||||
.entry(region.name().to_owned())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue