hyperapp
hyperapp copied to clipboard
@hyperapp/html: use a Proxy?
In modern browsers, can we use a Proxy
instead of hard coding all the element types?
I'm using this right now:
import { h } from "./lib/hyperapp.js";
const { main, h1, input, ul, li, button } = new Proxy(h, {
get(h, type) { return (props, children) => h(type, props, children) }
});
One more line of code, and you have support for custom elements:
const { hyperApp } = new Proxy(h, {
get(h, type) {
const name = type.replace(/([A-Z])/g, c => "-" + c.toLowerCase());
return (props, children) => h(name, props, children)
}
});
console.log(hyperApp({foo:"bar"}, ["baz"])); // type: "hyper-app"
Thoughts? :-)
In the past I've used a similar Proxy
technique for creating element functions on-the-fly. It certainly feels cleaner. Your one-liner for custom elements support is pretty neat !
However, if I'm not mistaken using a Proxy
comes at the cost of some performance, which to be fair is probably fine for a lot of cases.
My preference nowadays is to use h
directly, though.
However, if I'm not mistaken using a
Proxy
comes at the cost of some performance, which to be fair is probably fine for a lot of cases.
In this case, the Proxy is just a factory for the element creation functions - there is no run-time performance overhead once you've generated your functions.
My only reservation is this only works in modern browsers and Proxy can't be polyfilled.
It's good to know the Proxy
performance penalty is not a concern. That said, it looks like the primary advantage of this technique over the current technique is the custom elements support. I think @hyperapp/html
is tree-shakeable so code size for production shouldn't be a big difference in most cases. Though, maintaining a big clunky list of exported functions is annoying but I think that's a small price to pay to not have to worry about browser support.
I actually would like it if Proxy
was used but like you said, it only works where it works.
Unless you need IE11 support (which I don't think hyperapp supports anyway) Proxy is fine! I've used the approach suggested here in the past as well and really it's fine :)
The very small issues I've had with it are:
- Tree shaking (but now that I'm striving to be bundler-free, proxy is technically less to import)
- I didn't manage to declare types for the proxy. But that doesn't mean there isn't a way to do it ;)
- I prefer the one-liner: `import {div, p, text} from '@hyperapp/html' rather than the two-liner:
import html from '@hyperapp/html'
const {div, p, text} = html
Those aren't huge issues though and I could go either way. 🤷 😄
Custom elements is a nice plus. Now, the purpose here would be shipping less code with the package or would there be anything else?
I prefer the one-liner:
import {div, p, text} from '@hyperapp/html'
rather than the two-liner.
In the land of the multi-line, the one-liner is king.
One-liner is probably doable with export =
?
pf, using Proxy
since early beginning (looks like already more than 2 years):
https://gist.github.com/sergey-shpak/9266316d9abd550ddbeac2c88e5a0d22
@sergey-shpak great minds... heh. I too tried to get around the noisy empty attributes object you have to pass with every call - although, for some reason, my attempts seemed to break the official examples... children
aren't always an array, I think?
Such a small thing, but it would make the examples look much better. I guess it's a breaking change though...
@mindplay-dk the gist is pretty old I haven't updated it for a while, my current implementation is following:
import {h, text} from 'hyperapp'
/* Html factory */
export const html = new Proxy({}, {
get: (target, name) => name !== 'text'
? (attrs = {}, child) =>
!Array.isArray(attrs) && !attrs.tag
? h(name, attrs, child)
: h(name, {}, attrs)
: text
})
and the usage
const {div, span, text} = html
// no useless array for single element
// and no empty props object
div(text('Some text'))
// or something like
div([
span(text('some text')),
span(text('other text'))
])
// and common usage
div({class: 'active'}, text('no array for single child'))
*added later: works with [email protected], I haven't tested with other versions but should be ok