A less dom and more template approach
daaku opened this issue ยท 4 comments
Currently the way the rsx!
macro parses and outputs something akin to a psuedo-dom style set of element and attributes is similar to what React does. But the current use case of this crate is server side rendering (unless you have plans for this with wasm), a dom like structure is an implementation detail. With this in mind, it would be more performant to pass thru most of the template as static bytes to write calls, and splice in dynamic content where we find it.
As an example, for this component:
#[component]
fn Container<'kind, Children: Render>(
kind: &'kind str,
children: Children,
) {
rsx! {
<div class={kind}>
{children}
</div>
}
}
Old output:
let result = {
let Container { kind, children } = self;
{
::render::SimpleElement {
tag_name: "div",
attributes: {
let mut hm = std::collections::HashMap::<&str, &str>::new();
hm.insert("class", kind);
Some(hm)
},
contents: Some(children),
}
}
};
::render::Render::render_into(result, w)
New output:
let Container { kind, children } = self;
write!(w, b"<div class=")?;
::render::Render::render_into(kind, w)?;
write!(w, b">")?;
::render::Render::render_into(children, w)?;
write!(w, b"</div>")?;
Ok(())
I think there are other things to consider with this change, like automatic quoting of attribute, punning (which I independently am unsure of) etc. I'll figure that out as I make progress. I'm going to experiment with this idea and see how far I can take it without running into unforeseen issues.
Thoughts?
it would be more performant to pass thru most of the template as static bytes to write calls
That's probably true but I wonder if that can be optimized by the compiler? I'm not sure how strong it can get ๐ because SimpleElement
output should be kinda similar to the "new output". I made the SimpleElement
struct because it was easier for me, but if we can see a real performance gain we can maybe just write the bytes directly!
Regarding DOM-specific things, I agree that it might be weird to have this ideas baked in, but do you agree that it would be mostly used as an HTML template generator having this special case is better?
- If you want to have a "custom component" that renders whatever you want, you can use a struct/"function component". I imagine it like React Native, where you don't use lower case components at all. Everything is a "custom component", whether provided by the user or by the framework itself
- You can always return a string because it is
Render
- You can always just
write!
if you want to (like we do internally ๐) - As you mentioned the quoting, we also escape the strings, which I'm not sure we should always do, but probably need to do that automatically for HTML elements' attributes and (string) children so XSS won't be an issue if you use Render with a web framework
Also, lately I found maud which returns an owned string. Owned strings should be less efficient, memory wise, than what we do. But they say that practically it is faster. I don't think we should micro-optimize things that will hurt expressiveness and ease of use. What do you think about that?
(It's sometimes a problem to express feeling in writing, especially when having a language-barrier. Just to be on the safe side, These are genuine questions, would be happy to get your feedback ๐)
That's probably true but I wonder if that can be optimized by the compiler? I'm not sure how strong it can get smiley because
SimpleElement
output should be kinda similar to the "new output". I made theSimpleElement
struct because it was easier for me, but if we can see a real performance gain we can maybe just write the bytes directly!
I'm pretty sure the compiler can't optimize away the use of the HashMap
/ HashSet
-- but I'm no expert, will need to investigate.
Regarding DOM-specific things, I agree that it might be weird to have this ideas baked in, but do you agree that it would be mostly used as an HTML template generator having this special case is better?
To be clear, I'm not suggesting supporting non HTML things -- I'm just talking about the internal implementation to prefer passing bytes thru rather than parsing individual tags into nodes etc.
If you want to have a "custom component" that renders whatever you want, you can use a struct/"function component". I imagine it like React Native, where you don't use lower case components at all. Everything is a "custom component", whether provided by the user or by the framework itself
With what I'm suggesting, the way to write components will not change. In fact, I'm pretty sure most current code will just keep working (only punning is what I'm unsure of).
Also, lately I found maud which returns an owned string. Owned strings should be less efficient, memory wise, than what we do. But they say that practically it is faster.
This is interesting. I'm not sure what they tested exactly, but would be interesting to find some benchmarks and get clarity on the testing.
I don't think we should micro-optimize things that will hurt expressiveness and ease of use. What do you think about that?
Again I want to be clear that the ergonomics of using the library will not be affected by what I'm proposing!
(It's sometimes a problem to express feeling in writing, especially when having a language-barrier. Just to be on the safe side, These are genuine questions, would be happy to get your feedback smiley)
Absolutely! It's good to discuss these things and thanks for letting me know English isn't your primary language :) Feel free to be frank and direct!
I'm going to experiment with it, because I'm still convinced it will be easier/faster/better in various ways. I'll also write some benchmarks so we can get some real numbers to compare as well. If it doesn't work out that way, I'll be fine to discard the work :)
To be clear, I'm not suggesting supporting non HTML things -- I'm just talking about the internal implementation to prefer passing bytes thru rather than parsing individual tags into nodes etc.
Ah, that sums it up ๐ basically inlining SimpleElement
, no? I think it can be good ๐
But if that's too difficult and harder to test, maybe we can drop the HashMap/HashSet usages in favor of arrays of tuples (like I thought in #6)? Not sure if this can be optimized away either but it feels lighter ๐
I'll be happy to see the results ๐
Ah, that sums it up smile basically inlining
SimpleElement
, no? I think it can be good ๐
Yes, basically!
But if that's too difficult and harder to test, maybe we can drop the HashMap/HashSet usages in favor of arrays of tuples (like I thought in #6)? Not sure if this can be optimized away either but it feels lighter ๐
That might actually be something the compiler can optimize away!