Incremental performance improvements to element creation (#3169)

* enable interning

* intern tag names

* intern attribute keys and event listener types

* intern attribute values

* cache and clone elements

* clean up the node cloning version a bit

* use HashMap instead of Vec for element cache

* Revert "intern attribute values"

This reverts commit 28653c4660dcf1942fab3b0ad7d4c840b96e0f2a.

* add `enable-interning` feature to Yew that activates the same in wasm-bindgen

* remove interning feature
This commit is contained in:
Greg Johnston 2023-04-02 15:29:21 -04:00 committed by GitHub
parent 8086a73a21
commit bdf5712d96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 16 deletions

View File

@ -75,7 +75,7 @@ features = [
"WheelEvent",
"Window",
"HtmlScriptElement",
"SubmitEvent"
"SubmitEvent",
]
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
@ -89,11 +89,7 @@ trybuild = "1"
[dev-dependencies.web-sys]
version = "0.3"
features = [
"ShadowRootInit",
"ShadowRootMode",
"HtmlButtonElement"
]
features = ["ShadowRootInit", "ShadowRootMode", "HtmlButtonElement"]
[features]
ssr = ["dep:html-escape", "dep:base64ct", "dep:bincode"]

View File

@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::ops::Deref;
use indexmap::IndexMap;
use wasm_bindgen::JsValue;
use wasm_bindgen::{intern, JsValue};
use web_sys::{Element, HtmlInputElement as InputElement, HtmlTextAreaElement as TextAreaElement};
use yew::AttrValue;
@ -163,9 +163,9 @@ impl Attributes {
fn set(el: &Element, key: &str, value: &str, apply_as: ApplyAttributeAs) {
match apply_as {
ApplyAttributeAs::Attribute => {
el.set_attribute(key, value).expect("invalid attribute key")
}
ApplyAttributeAs::Attribute => el
.set_attribute(intern(key), value)
.expect("invalid attribute key"),
ApplyAttributeAs::Property => {
let key = JsValue::from_str(key);
let value = JsValue::from_str(value);
@ -177,7 +177,7 @@ impl Attributes {
fn remove(el: &Element, key: &str, apply_as: ApplyAttributeAs) {
match apply_as {
ApplyAttributeAs::Attribute => el
.remove_attribute(key)
.remove_attribute(intern(key))
.expect("could not remove attribute"),
ApplyAttributeAs::Property => {
let key = JsValue::from_str(key);

View File

@ -4,6 +4,8 @@ mod attributes;
mod listeners;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::hint::unreachable_unchecked;
use std::ops::DerefMut;
@ -252,9 +254,31 @@ impl VTag {
.create_element_ns(namespace, tag)
.expect("can't create namespaced element for vtag")
} else {
document()
.create_element(tag)
.expect("can't create element for vtag")
thread_local! {
static CACHED_ELEMENTS: RefCell<HashMap<String, Element>> = RefCell::new(HashMap::with_capacity(32));
}
CACHED_ELEMENTS.with(|cache| {
let mut cache = cache.borrow_mut();
let cached = cache.get(tag).map(|el| {
el.clone_node()
.expect("couldn't clone cached element")
.unchecked_into::<Element>()
});
cached.unwrap_or_else(|| {
let to_be_cached = document()
.create_element(tag)
.expect("can't create element for vtag");
cache.insert(
tag.to_string(),
to_be_cached
.clone_node()
.expect("couldn't clone node to be cached")
.unchecked_into(),
);
to_be_cached
})
})
}
}
}

View File

@ -8,7 +8,7 @@ use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use wasm_bindgen::{intern, JsCast, UnwrapThrowExt};
use web_sys::{
AddEventListenerOptions, Element, Event, EventTarget as HtmlEventTarget, ShadowRoot,
};
@ -157,7 +157,7 @@ impl EventListener {
target
.add_event_listener_with_callback_and_add_event_listener_options(
&event_type,
intern(&event_type),
callback.as_ref().unchecked_ref(),
&options,
)