Comments (8)
I was building a web client for a project at work. My first choice was React+Redux, since that's what we already use, but I wanted something more lightweight and without the frameworkyness of React.
I made the first prototype using Elm, which I really like, but it was too difficult to integrate with 3rd party components, e.g. CodeMirror.
yo-yo/choo
yo-yo is an abstraction on top of bel and morphodom (which mutates DOM nodes directly) whereas I preferred a virtual DOM approach like Snabbdom.
There's choo too, an abstraction on top of yo-yo with a much nicer API.
- Since they're dealing with real HTML elements, they had to come up with a way to have lifecycle events for DOM nodes using the MutationObserver API which seems like code smell to me.
- They don't support CSS out of the box either, so I had to use dom-css.
- Couldn't agree with some of choo API choices like namespaces and their router design.
- Too hard to integrate with 3rd party components, involving the creation of a portal, reminiscent of Elm ports.
Hyperapp
~1kb. It's almost vanilla. No deps, other than Hyperx (itself no deps) which is how we write HTML using JavaScript, but without breaking JavaScript. You can still use JSX if you want.
Hyperapp has Elm-like state management, a router and a tiny virtual dom implementation with SVG support, inline CSS, boolean HTML props and create, update and remove events for DOM nodes.
Integrating 3rd party components with Hyperapp is easy too. See this example with CodeMirror.
Compare
Let's see a input box + heading example.
Hyperapp
const { app, html } = require("hyperapp")
app({
model: "",
update: {
text: (_, value) => value
},
view: (model, msg) => html`
<div>
<h1>Hello ${model}</h1>
<input oninput=${e => msg.text(e.target.value)} />
</div>`
})
Choo
const html = require("choo/html")
const choo = require("choo")
const app = choo()
app.model({
state: { title: "" },
reducers: {
update: (state, data) => ({ title: data })
}
})
const mainView = (state, prev, send) => html`
<main>
<h1>Hello ${state.title}</h1>
<input type="text" oninput=${e => send("update", e.target.value)}>
</main>
`
app.router(["/", mainView])
const tree = app.start()
document.body.appendChild(tree)
Mercury
var document = require("global/document")
var hg = require("mercury")
var h = require("mercury").h
function App() {
var state = hg.struct({
text: hg.value(""),
handles: hg.value(null)
})
state.handles.set(hg.handles({
change: setText
}, state))
return state
}
function inputBox(value, sink) {
return h("input.input", {
value: value,
name: "text",
type: "text",
"ev-event": hg.changeEvent(sink)
})
}
App.render = function render(state) {
return h("div", [
h("p.content", "Hello " + state.text),
h("p", [
"Change it here: ",
inputBox(state.text, state.handles.change)
])
])
}
function setText(state, data) {
state.text.set(data.text)
}
hg.app(document.body, App(), App.render)
Cyclejs
const { run } = require("@cycle/xstream-run")
const { div, label, input, hr, h1, makeDOMDriver} = require("@cycle/dom")
function main(sources) {
const sinks = {
DOM: sources.DOM.select(".field").events("input")
.map(ev => ev.target.value)
.startWith("")
.map(name =>
div([
label("Name:"),
input(".field", { attrs: { type: "text" } }),
hr(),
h1("Hello " + name),
])
)
}
return sinks
}
run(main, {
DOM: makeDOMDriver("#app-container")
})
Mithril
Mithril is really clean β€οΈ
State flow is not Elm/Redux-like, though, you're on your own!
const m = require("mithril")
var name = ""
const helloWorld = {
view: _ => [
m("h1", "Hello " + name),
m("input", {
value: name,
oninput: e => name = e.target.value
})
]
}
m.mount(document.body, helloWorld)
from hyperapp.
hey @jorgebucaran I know you from snabbdom
:)
I'm amazed by the simplicity of the code, I really like it.
Some comments/questions in my head right now:
(1) are you planning to add tests to the library? I wouldn't feel comfortable using it in production withoutβ¦
(2) since this is somewhat similar to choo
(I could recognize it before reading here) it'd be lovely if you included the above explanation in the README
(3) the only thing I would miss are "hooks" for elements (like in snabbdom
), (correct me if I'm wrong)
from hyperapp.
Yes I was thinking the same things @acstll :). Very nice experiment in finding the most minimal implementation of the elm pattern, and the code is so simple. Was also wondering about what tests would look like.
from hyperapp.
Thanks for the awesome explanation @jorgebucaran. Did you end up using this library for your work project? How has that been? Do you have plans for the future direction of this repo?
I agree with your points, that's basically where I'm at too. yoyo and choo I think are very cool experiments, but some of the design decisions have created some extra complexity. The namespace/state pattern, and direct dom mutation have made some things difficult (not to say that I've made something nicer). Though also choo has grown a lot, which may have added more to the scope.
Can you elaborate on the use of MutationObserver and code smell?
from hyperapp.
Did you end up using this library for your work project?
Yes. I upgraded our project to use Hyperapp from yo-yo.
Can you elaborate on the use of MutationObserver and code smell?
It seems lifecycle events don't make sense if you are working directly with the DOM, e.g. using morphodom. The DOM has no onload/onunload for individual elements.
In order to support on load/unload events for DOM elements they use the MutationObserver API which adds more complexity to their architecture (MutationObserver is only supported in IE11 too).
Are you planning to add tests to the library?
Yes. Soon! π
Include the above explanation in the README / mention choo
Totally. Maybe we can add a FAQ in the future!
Does Hyperapp support hooks like Snabbdom?
Yes. It's just missing in the docs. The term we'll use is _lifecycle methods, since we're already using hooks for something else.
Lifecycle Methods
- oncreate
- onupdate
- onremove
Examples
app({
view: _ => html`
<div oncreate=${e => console.log(e)}>Hi.</div>`
})
Advanced Example
app({
model: 0,
update: {
toggle: model => model === 0 ? 1 : model === 1 ? 2 : 0
},
view: (model, msg) => html`
<div>
${model > 0
? html`
<ul>
<li style=${{ color: model === 1 ? "inherit" : "deepskyblue" }}>
Leonardo
</li>
<li style=${{ color: model === 1 ? "inherit" : "orange" }}>
Michelangelo
</li>
<li style=${{ color: model === 1 ? "inherit" : "red" }}>
Raphael
</li>
<li style=${{ color: model === 1 ? "inherit" : "purple" }}
onupdate=${_ => console.log("UPDATE")}
oncreate=${_ => console.log("CREATE")}
onremove=${_ => console.log("REMOVE")}>Donatello
</li>
</ul>`
: html`
<ul>
<li>Krang</li>
</ul>`
}
<button onclick=${msg.toggle}>Mutate!</button>
</div>`,
})
from hyperapp.
A lot has changed since you opened this issue @nichoth π.
The core values of the project still remain to be a small (or the smallest) JavaScript frontend framework that is useful out of the box and implements a sane subset of the Elm Architecture, supports Hyperx, JSX and it's easy to learn.
from hyperapp.
I would like to know, too.
from hyperapp.
Bonus: The virtual DOM approach is faster in most benchmarks compared to morphodom.
from hyperapp.
Related Issues (20)
- A way to insert raw Html HOT 1
- TypeError: can't access property 0, newSubs is null, when setting the state to undefined. HOT 4
- Issue with null-vnodes HOT 1
- prevent rerender node HOT 2
- The dispatch initializer ends in an endless loop on init when dispatching any action HOT 7
- Injected classes gets removed when using object/array to define class props HOT 1
- hyperapp version HOT 3
- Memo Data Gotcha HOT 5
- Confusing doc for actions -> wrapped actions HOT 5
- Passing arguments to init HOT 4
- [Question] Headless mode is still possible? HOT 1
- Destroying a child app HOT 8
- @hyperapp/html: use a Proxy? HOT 9
- Actions returning other Actions HOT 5
- Compile template tag to hyperscript HOT 17
- A challenge to hyperapp community HOT 1
- Has 2.0 been dropped from development? HOT 3
- oldSub[2] is not a function HOT 3
- Cannot read properties of null (reading 'length') HOT 5
- Unlikely Use Case bug in HTML and SVG Packages HOT 9
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from hyperapp.