As an aside, I really like the class name and text content ergonomics (e.g div.someclass, span:some content). Reminiscent of pug/jade
I 'invented' the concept for this back in 2011, and it was used (as a proprietary lib) in various startups. Even though many similar open source libs have been released since, and boy have I tried a lot of them, none have been able to capture the elegance and DX of what we had back then. I might be biased though. :-)
So I started creating a cleaned-up, modern, TypeScript, open source implementation for the concept about five years ago. After many iterations, working on the project on and off, I'm finally happy with its API and the developer experience it offers. I'm calling it 1.0!
The concept: It uses many small, anonymous functions for emitting DOM elements, and automatically reruns them when their underlying proxied data changes. This proxied data can be anything from simple values to complex, typed, and deeply nested data structures.
As I'm currently free to spend my time on labors of love like this, I'm planning to expand the ecosystem around this to include synchronizing data with a remote server/database, and to make CRUD apps very rapid and perhaps even pleasurable to implement.
I've celebrated 1.0 by creating a tutorial with editable interactive examples! https://aberdeenjs.org/Tutorial/
I would love to hear your feedback. The first few people to actually give Aberdeen a shot can expect fanatical support from me! :-)
As an aside, I really like the class name and text content ergonomics (e.g div.someclass, span:some content). Reminiscent of pug/jade
Also, in order to transform JSX into individual rerunnable functions, we'd need a whole different transpiler. I like being able to code browser-runnable JavaScript directly.
To each their own. :-)
I don't think you would. `<Component prop="example" />` gets converted by current transpilers into `jsx(Component, { prop: "example" })`. The `Component` itself is passed as is. In the case of Components that are just functions, that passes the function as-is, as a function you can just call as needed.
JSX was built for "rerunnable functions". It's a lot of how React works under the hood.
This JSX:
<section><h1>Welcome</h1>{data.enabled ? <input /> : "disabled"}</section>
Which becomes this with Babel: _jsx("section", {children: [
_jsx("h1", {children: "Welcome"}),
data.enabled ? _jsx("input", {}) : "disabled"
]})
But we'd need something like this to fit Aberdeen: _jsx("section", ()=>{
_jsx("h1", ()=>{_jsx("Welcome");});
if (data.enabled) _jsx("input", {}) else _jsx("disabled");
}})
In React you can, with some effort, limit virtual DOM rerenders to a single component. Aberdeen rerenders fractions of components by default, if that's all that needs to happen. That's why it works well operating directly on the actual DOM, without a virtual DOM inbetween. <section><h1>Welcome</h1>{ () => data.enabled ? <input /> : "disabled" }</section>
That doesn't even look half bad to me. It looks really close to many of your other examples.> In React you can, with some effort, limit virtual DOM rerenders to a single component. Aberdeen rerenders fractions of components by default, if that's all that needs to happen. That's why it works well operating directly on the actual DOM, without a virtual DOM inbetween.
A lot of that depends on what your model of a "component" is. React will rerender fractions of a component in advanced areas such as a Error Boundaries and Suspense.
Also, for what little it is worth, my JSX-based library isn't a virtual DOM, operates directly on the actual DOM, and generally renders components once and only once in their lifetime, because it binds all changes even more directly to specific DOM properties.
render() {
if (this.dontNeedToRender) {
return null
}
}
Now, because hooks can't be skipped, react developers jump through many hoops to use an if statement during rendering.It unfortunately can't, because by the time the jsx function gets a chance to run, the expression has already been evaluated, whereas the whole point of a framework like this is to be able to run that evaluation repeatedly.
So what you'd need to write to get that to work would be something like
<div>{() => user.name}</div>
for every single case where you're interpolating a value, which starts looking kind of ugly. It also has its own pitfalls. For example, your example with a ternary in it would unnecessarily rerender (i.e. completely unmount and recreate) the child elements if the input changes but the conditional still evaluates to the same thing. (E.g. if data.enabled was 1 and we set it to 2, what we want is to have no change in the DOM, but what we'd get the input element being unmounted and remounted.)There's also just the general issue that JSX requires some sort of transform. This isn't necessarily bad - I use SolidJS a lot which explicitly makes use of the chance to optimise the JSX rendering at compile time - but there are situations where you want to avoid that, and having a syntax that's focused on the "no compile step" case is useful.