♻️ (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)))
|
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 >**************************************************************************
|
// **< Child BUILDER >**************************************************************************
|
||||||
|
|
||||||
/// Establece un componente nuevo, o lo vacía.
|
/// 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.
|
/// Convierte un [`Embed<C>`] en un [`Child`], consumiendo el componente tipado.
|
||||||
///
|
///
|
||||||
/// Útil cuando se tiene un [`Embed`] para añadir a una lista [`Children`]:
|
/// Ú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()`].
|
/// Convierte cualquier componente en un [`Child`], equivalente a [`Child::with()`].
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(component: T) -> Self {
|
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
|
/// Convierte un componente en [`ChildOp::Add`], permitiendo pasar componentes directamente a
|
||||||
/// métodos como [`Children::with_child`] sin envolverlos explícitamente.
|
/// métodos como [`Children::with_child`] sin envolverlos explícitamente.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -7,26 +7,13 @@ use parking_lot::RwLock;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, LazyLock};
|
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
|
// Se comparten como `Arc<dyn Component>`. El prototipo no se clona al registrarse ni al ensamblar
|
||||||
// llamada a [`as_child`](Self::as_child) produce un clon fresco del estado original, de modo que
|
// la región (sólo se clona el `Arc`, barato). En cambio, sí se realiza un clonado del componente en
|
||||||
// cada página renderiza el componente desde su estado inicial sin acumular mutaciones de peticiones
|
// `Child::render()`, cuando cada petición necesita su propia copia mutable para pasar por `setup()`
|
||||||
// anteriores.
|
// desde un estado inicial limpio.
|
||||||
trait ComponentGlobal: Send + Sync {
|
type RegionComponents = HashMap<String, Vec<Arc<dyn Component>>>;
|
||||||
// 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>>>;
|
|
||||||
|
|
||||||
// Regiones globales con prototipos asociados a un tema específico.
|
// Regiones globales con prototipos asociados a un tema específico.
|
||||||
static THEME_REGIONS: LazyLock<RwLock<HashMap<UniqueId, RegionComponents>>> =
|
static THEME_REGIONS: LazyLock<RwLock<HashMap<UniqueId, RegionComponents>>> =
|
||||||
|
|
@ -64,23 +51,22 @@ impl ChildrenInRegions {
|
||||||
///
|
///
|
||||||
/// Se recogen desde tres fuentes disponibles, en el siguiente orden:
|
/// 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.
|
/// 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
|
/// 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.
|
/// 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
|
/// 3. Prototipos del tema activo, exclusivos del tema en curso. Se comparten igual que los
|
||||||
/// que llegan a `setup()` con el mismo estado inicial.
|
/// comunes.
|
||||||
pub fn assemble_region(&mut self, theme: ThemeRef, region: RegionRef) -> Children {
|
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 mut result = Children::new();
|
||||||
|
|
||||||
|
let region_name = region.name();
|
||||||
|
|
||||||
// 1. Prototipos globales comunes.
|
// 1. Prototipos globales comunes.
|
||||||
if let Some(protos) = common.get(region_name) {
|
if let Some(global_protos) = COMMON_REGIONS.read().get(region_name) {
|
||||||
for proto in protos {
|
for proto in global_protos {
|
||||||
result.add(proto.as_child());
|
result.add(Child::from_arc(Arc::clone(proto)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2. Componentes propios de la página: se mueven, no se clonan.
|
// 2. Componentes propios de la página: se mueven, no se clonan.
|
||||||
|
|
@ -90,11 +76,11 @@ impl ChildrenInRegions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 3. Prototipos del tema activo.
|
// 3. Prototipos del tema activo.
|
||||||
if let Some(theme_map) = themed.get(&theme.type_id()) {
|
if let Some(theme_region) = THEME_REGIONS.read().get(&theme.type_id())
|
||||||
if let Some(protos) = theme_map.get(region_name) {
|
&& let Some(theme_protos) = theme_region.get(region_name)
|
||||||
for proto in protos {
|
{
|
||||||
result.add(proto.as_child());
|
for proto in theme_protos {
|
||||||
}
|
result.add(Child::from_arc(Arc::clone(proto)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,8 +154,8 @@ impl InRegion {
|
||||||
/// html! { "Aviso legal" }
|
/// html! { "Aviso legal" }
|
||||||
/// }));
|
/// }));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add(&self, component: impl Component + Clone + 'static) -> &Self {
|
pub fn add(&self, component: impl Component) -> &Self {
|
||||||
let proto: Arc<dyn ComponentGlobal> = Arc::new(component);
|
let proto: Arc<dyn Component> = Arc::new(component);
|
||||||
match self {
|
match self {
|
||||||
InRegion::Content => Self::add_to_common(&CoreRegion::Content, proto),
|
InRegion::Content => Self::add_to_common(&CoreRegion::Content, proto),
|
||||||
InRegion::Global(region) => Self::add_to_common(*region, proto),
|
InRegion::Global(region) => Self::add_to_common(*region, proto),
|
||||||
|
|
@ -187,7 +173,7 @@ impl InRegion {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add_to_common(region: RegionRef, proto: Arc<dyn ComponentGlobal>) {
|
fn add_to_common(region: RegionRef, proto: Arc<dyn Component>) {
|
||||||
COMMON_REGIONS
|
COMMON_REGIONS
|
||||||
.write()
|
.write()
|
||||||
.entry(region.name().to_owned())
|
.entry(region.name().to_owned())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue