Please check it out: https://github.com/pragmagic/karax
Thanks to Nim's metaprogramming, static typing and JS backend we've been able to produce an SPA framework in a short amount of time, and performance is on par with the big players out there. Why use the best tool for the job when you can use the best tool instead? :-)
Looks neat, might have to give this a go for some upcoming stuff I was going to write in React.
Can I ask what exactly the "Benchmark" graph is meant to show in the readme? Because this isn't very descriptive ;)
Indeed, great idea!
Did you had a look at Vue for inspiration? Many people consider it the next big thing after React, and in terms of performance it has already raised the bar a bit.
But performance is typically not the most important aspect for choosing a JS framework. Probably karax would benefit the most by making it more accessible to JS users. Some random questions (not necessarily to be answered here, just points for documentation).
Did you had a look at Vue for inspiration? Many people consider it the next big thing after React, and in terms of performance it has already raised the bar a bit.
Yeah, I only used Vue version 1, so I know what to stay away from. I looked at Elm for a major source of inspiration.
How to interact with third party JS libraries?
We're making Karax work with emscripten and strive for good interop with Nim code, not JS code. ;-)
Why aren't components split into template/code(/style)?
Because such a split only produces artificial complexity.
How do components communicate?
They don't. Components should be stateless. I think we should use a different name, Karax's components are not what others call "components".
What about the tooling, i.e., how does building / bundling / browser test running / debugging (source maps) work?
Source maps are in the works and "bundling" doesn't need anything, it's generated JS code, move it to where you want.
An idea for the documentation would be to map JS artefacts like npm/webpack/mocha/karma/selenium to possible solutions.
I don't know what that means.
All cstring here, I'm scared -- wait, it's not even compiling to C? I think I get now why it has to be like that, but getting rid of that c could definitely reduce the scare factor for newbies
Would it help to rename it to jsstring?
Would it help to rename it to jsstring?
I think what scares people about "cstring" are two things:
"jsstring" alias would fix the first point, but not the second one.
@Araq: Sound very interesting, really looking forward in which direction this will develop. +1 for the Elm inspiration and not following the the one-file-component path. And compared to Elm, the Nim syntax really shines for the templating stuff.
I've been wondering for some time, if it would be possible to achieve a design which is even closer to the Elm architecture? On first glance it would be so nice to write stuff like this:
type:
Model = object
# ...
Msg = object
# typically an object variant
proc init(): Model = ...
proc update(msg: Msg, model: Model): Model = ...
proc view[Msg](model): Html[Msg] = ...
karax.runMain(init, update, view)
Are there any plans to go in that direction or are there technical obstacles which prevent such a design?
In fact, it is maybe not even too far away:
include karaxprelude
import jstrutils, dom, future
type
Model = ref object
toggle: bool
EventHandler = (Event, VNode) -> void
EventHandlerModel = (Model, Event, VNode) -> void
proc curry(handler: EventHandlerModel, model: Model): EventHandler =
result = (ev: Event, n: VNode) => handler(model, ev, n)
proc onClick(model: Model, ev: Event, n: VNode) =
kout(ev)
kout(model)
model.toggle = model.toggle xor true
proc view(model: Model): VNode =
result = buildHtml():
tdiv:
button(onclick=curry(onClick, model)):
text "click me"
tdiv:
text "Toggle state:"
tdiv:
if model.toggle:
text "true"
else:
text "false"
proc runMain() =
var model = Model(toggle: true)
proc renderer(): VNode =
# var model = Model(toggle: true) # init here does not work
view(model)
setRenderer renderer
runMain()
At first I was initializing the model in the renderer proc. This had the result that the view never updated -- it's not immediately clear to me why it has to be in the outer scope.
Adding an Elm-like message approach should also be possible by constructing some Msg in each event handler and calling an update function. Okay, in contrast to Elm the model is updated in-place, but maybe it is even beneficial to keep the model update close to how it works natively.
The only issue is that every event handler has to be curried. So my question probably becomes: Is it possible to automatize this "injection" of the model into the event handlers, so that an event handler (Model, Event, VNode) -> void can be passed directly to on....
Really interesting stuff.
I'm unable to compile the examples with nim 0.17.0