Skip to content

Commit

Permalink
Make VNode cheap to clone
Browse files Browse the repository at this point in the history
  • Loading branch information
cecton committed Sep 30, 2023
1 parent 194a1e6 commit fd1187f
Show file tree
Hide file tree
Showing 11 changed files with 62 additions and 27 deletions.
2 changes: 1 addition & 1 deletion packages/yew-macro/src/html_tree/html_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ impl ToTokens for HtmlElement {
quote! {
::std::convert::Into::<::yew::virtual_dom::VNode>::into(
::yew::virtual_dom::VTag::__new_other(
::std::borrow::Cow::<'static, ::std::primitive::str>::Borrowed(#name),
::yew::virtual_dom::AttrValue::from(#name),
#node_ref,
#key,
#attributes,
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/src/html/conversion/to_html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ where

#[inline(always)]
fn into_html(self) -> Html {
VNode::VComp(self.into())
VNode::VComp(Rc::new(self.into()))
}
}

Expand Down
3 changes: 3 additions & 0 deletions packages/yew/src/virtual_dom/listeners.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::html::ImplicitClone;
use std::rc::Rc;

/// The [Listener] trait is an universal implementation of an event listener
Expand Down Expand Up @@ -168,6 +169,8 @@ pub enum Listeners {
Pending(Box<[Option<Rc<dyn Listener>>]>),
}

impl ImplicitClone for Listeners {}

impl PartialEq for Listeners {
fn eq(&self, rhs: &Self) -> bool {
use Listeners::*;
Expand Down
19 changes: 10 additions & 9 deletions packages/yew/src/virtual_dom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod vtag;
pub mod vtext;

use std::hint::unreachable_unchecked;
use std::rc::Rc;

use indexmap::IndexMap;

Expand Down Expand Up @@ -204,7 +205,7 @@ pub enum Attributes {

/// IndexMap is used to provide runtime attribute deduplication in cases where the html! macro
/// was not used to guarantee it.
IndexMap(IndexMap<AttrValue, (AttrValue, ApplyAttributeAs)>),
IndexMap(Rc<IndexMap<AttrValue, (AttrValue, ApplyAttributeAs)>>),
}

impl Attributes {
Expand Down Expand Up @@ -233,31 +234,31 @@ impl Attributes {
macro_rules! unpack {
() => {
match self {
Self::IndexMap(m) => m,
Self::IndexMap(m) => Rc::make_mut(m),
// SAFETY: unreachable because we set self to the `IndexMap` variant above.
_ => unsafe { unreachable_unchecked() },
}
};
}

match self {
Self::IndexMap(m) => m,
Self::IndexMap(m) => Rc::make_mut(m),
Self::Static(arr) => {
*self = Self::IndexMap(
*self = Self::IndexMap(Rc::new(
arr.iter()
.map(|(k, v, ty)| ((*k).into(), ((*v).into(), *ty)))
.collect(),
);
));
unpack!()
}
Self::Dynamic { keys, values } => {
*self = Self::IndexMap(
*self = Self::IndexMap(Rc::new(
std::mem::take(values)
.iter_mut()
.zip(keys.iter())
.filter_map(|(v, k)| v.take().map(|v| (AttrValue::from(*k), v)))
.collect(),
);
));
unpack!()
}
}
Expand All @@ -270,7 +271,7 @@ impl From<IndexMap<AttrValue, AttrValue>> for Attributes {
.into_iter()
.map(|(k, v)| (k, (v, ApplyAttributeAs::Attribute)))
.collect();
Self::IndexMap(v)
Self::IndexMap(Rc::new(v))
}
}

Expand All @@ -280,7 +281,7 @@ impl From<IndexMap<&'static str, AttrValue>> for Attributes {
.into_iter()
.map(|(k, v)| (AttrValue::Static(k), (v, ApplyAttributeAs::Attribute)))
.collect();
Self::IndexMap(v)
Self::IndexMap(Rc::new(v))
}
}

Expand Down
3 changes: 3 additions & 0 deletions packages/yew/src/virtual_dom/vlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::ops::{Deref, DerefMut};
use std::rc::Rc;

use super::{Key, VNode};
use crate::html::ImplicitClone;

#[derive(Clone, Copy, Debug, PartialEq)]
enum FullyKeyedState {
Expand All @@ -23,6 +24,8 @@ pub struct VList {
pub key: Option<Key>,
}

impl ImplicitClone for VList {}

impl PartialEq for VList {
fn eq(&self, other: &Self) -> bool {
self.key == other.key && self.children == other.children
Expand Down
19 changes: 11 additions & 8 deletions packages/yew/src/virtual_dom/vnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
use std::cmp::PartialEq;
use std::iter::FromIterator;
use std::rc::Rc;
use std::{fmt, mem};

use web_sys::Node;

use super::{Key, VChild, VComp, VList, VPortal, VSuspense, VTag, VText};
use crate::html::BaseComponent;
use crate::html::{BaseComponent, ImplicitClone};
use crate::virtual_dom::VRaw;
use crate::AttrValue;

Expand All @@ -16,25 +17,27 @@ use crate::AttrValue;
#[must_use = "html does not do anything unless returned to Yew for rendering."]
pub enum VNode {
/// A bind between `VTag` and `Element`.
VTag(Box<VTag>),
VTag(Rc<VTag>),
/// A bind between `VText` and `TextNode`.
VText(VText),
/// A bind between `VComp` and `Element`.
VComp(VComp),
VComp(Rc<VComp>),
/// A holder for a list of other nodes.
VList(VList),
/// A portal to another part of the document
VPortal(VPortal),
/// A holder for any `Node` (necessary for replacing node).
VRef(Node),
/// A suspendible document fragment.
VSuspense(VSuspense),
VSuspense(Rc<VSuspense>),
/// A raw HTML string, represented by [`AttrValue`](crate::AttrValue).
///
/// Also see: [`VNode::from_html_unchecked`]
VRaw(VRaw),
}

impl ImplicitClone for VNode {}

impl VNode {
pub fn key(&self) -> Option<&Key> {
match self {
Expand Down Expand Up @@ -126,21 +129,21 @@ impl From<VList> for VNode {
impl From<VTag> for VNode {
#[inline]
fn from(vtag: VTag) -> Self {
VNode::VTag(Box::new(vtag))
VNode::VTag(Rc::new(vtag))
}
}

impl From<VComp> for VNode {
#[inline]
fn from(vcomp: VComp) -> Self {
VNode::VComp(vcomp)
VNode::VComp(Rc::new(vcomp))
}
}

impl From<VSuspense> for VNode {
#[inline]
fn from(vsuspense: VSuspense) -> Self {
VNode::VSuspense(vsuspense)
VNode::VSuspense(Rc::new(vsuspense))
}
}

Expand All @@ -156,7 +159,7 @@ where
COMP: BaseComponent,
{
fn from(vchild: VChild<COMP>) -> Self {
VNode::VComp(VComp::from(vchild))
VNode::VComp(Rc::new(VComp::from(vchild)))
}
}

Expand Down
5 changes: 4 additions & 1 deletion packages/yew/src/virtual_dom/vportal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
use web_sys::{Element, Node};

use super::VNode;
use crate::html::ImplicitClone;

#[derive(Debug, Clone, PartialEq)]
pub struct VPortal {
/// The element under which the content is inserted.
pub host: Element,
/// The next sibling after the inserted content. Most be a child of `host`.
/// The next sibling after the inserted content. Must be a child of `host`.
pub inner_sibling: Option<Node>,
/// The inserted node
pub node: Box<VNode>,
}

impl ImplicitClone for VPortal {}

impl VPortal {
/// Creates a [VPortal] rendering `content` in the DOM hierarchy under `host`.
pub fn new(content: VNode, host: Element) -> Self {
Expand Down
3 changes: 3 additions & 0 deletions packages/yew/src/virtual_dom/vraw.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::html::ImplicitClone;
use crate::AttrValue;

/// A raw HTML string to be used in VDOM.
Expand All @@ -6,6 +7,8 @@ pub struct VRaw {
pub html: AttrValue,
}

impl ImplicitClone for VRaw {}

impl From<AttrValue> for VRaw {
fn from(html: AttrValue) -> Self {
Self { html }
Expand Down
3 changes: 3 additions & 0 deletions packages/yew/src/virtual_dom/vsuspense.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{Key, VNode};
use crate::html::ImplicitClone;

/// This struct represents a suspendable DOM fragment.
#[derive(Clone, Debug, PartialEq)]
Expand All @@ -13,6 +14,8 @@ pub struct VSuspense {
pub(crate) key: Option<Key>,
}

impl ImplicitClone for VSuspense {}

impl VSuspense {
pub fn new(children: VNode, fallback: VNode, suspended: bool, key: Option<Key>) -> Self {
Self {
Expand Down
27 changes: 20 additions & 7 deletions packages/yew/src/virtual_dom/vtag.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! This module contains the implementation of a virtual element node [VTag].
use std::borrow::Cow;
use std::cmp::PartialEq;
use std::marker::PhantomData;
use std::mem;
Expand All @@ -10,7 +9,7 @@ use std::rc::Rc;
use web_sys::{HtmlInputElement as InputElement, HtmlTextAreaElement as TextAreaElement};

use super::{ApplyAttributeAs, AttrValue, Attributes, Key, Listener, Listeners, VNode};
use crate::html::{IntoPropValue, NodeRef};
use crate::html::{ImplicitClone, IntoPropValue, NodeRef};

/// SVG namespace string used for creating svg elements
pub const SVG_NAMESPACE: &str = "http://www.w3.org/2000/svg";
Expand All @@ -22,9 +21,17 @@ pub const MATHML_NAMESPACE: &str = "http://www.w3.org/1998/Math/MathML";
pub const HTML_NAMESPACE: &str = "http://www.w3.org/1999/xhtml";

/// Value field corresponding to an [Element]'s `value` property
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq)]
pub(crate) struct Value<T>(Option<AttrValue>, PhantomData<T>);

impl<T> Clone for Value<T> {
fn clone(&self) -> Self {
Self::new(self.0.clone())
}
}

impl<T> ImplicitClone for Value<T> {}

impl<T> Default for Value<T> {
fn default() -> Self {
Self::new(None)
Expand Down Expand Up @@ -68,6 +75,8 @@ pub(crate) struct InputFields {
pub(crate) checked: Option<bool>,
}

impl ImplicitClone for InputFields {}

impl Deref for InputFields {
type Target = Value<InputElement>;

Expand Down Expand Up @@ -111,12 +120,14 @@ pub(crate) enum VTagInner {
/// Fields for all other kinds of [VTag]s
Other {
/// A tag of the element.
tag: Cow<'static, str>,
tag: AttrValue,
/// children of the element.
children: VNode,
},
}

impl ImplicitClone for VTagInner {}

/// A type for a virtual
/// [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element)
/// representation.
Expand All @@ -133,10 +144,12 @@ pub struct VTag {
pub key: Option<Key>,
}

impl ImplicitClone for VTag {}

impl VTag {
/// Creates a new [VTag] instance with `tag` name (cannot be changed later in DOM).
pub fn new(tag: impl Into<Cow<'static, str>>) -> Self {
let tag: Cow<'static, str> = tag.into();
pub fn new(tag: impl Into<AttrValue>) -> Self {
let tag = tag.into();
Self::new_base(
match &*tag.to_ascii_lowercase() {
"input" => VTagInner::Input(Default::default()),
Expand Down Expand Up @@ -226,7 +239,7 @@ impl VTag {
#[doc(hidden)]
#[allow(clippy::too_many_arguments)]
pub fn __new_other(
tag: Cow<'static, str>,
tag: AttrValue,
node_ref: NodeRef,
key: Option<Key>,
// at bottom for more readable macro-expanded coded
Expand Down
3 changes: 3 additions & 0 deletions packages/yew/src/virtual_dom/vtext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::cmp::PartialEq;

use super::AttrValue;
use crate::html::ImplicitClone;

/// A type for a virtual
/// [`TextNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode)
Expand All @@ -13,6 +14,8 @@ pub struct VText {
pub text: AttrValue,
}

impl ImplicitClone for VText {}

impl VText {
/// Creates new virtual text node with a content.
pub fn new(text: impl Into<AttrValue>) -> Self {
Expand Down

0 comments on commit fd1187f

Please sign in to comment.