hyperapp ?
- https://github.com/hyperapp/hyperapp
- 300行で書かれたReact/ReduxライクなFW
dependencies: {}
サンプルコード(カウンター)
index.js
app({
model: 0,
actions: {
add: model => model + 1,
sub: model => model - 1
},
view: (model, actions) =>
<div>
<button onClick={actions.add}>
+
</button>
<h1>{model}</h1>
<button onClick={actions.sub}>
-
</button>
</div>
})
index.html
<!doctype html>
<html lang="en">
<head>
<title>Hello HyperApp</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
hyperappが持っている機能
- 宣言的にUIを記述
- VirualDOMみたいに差分だけ更新する
- Reduxみたいな状態管理
- plugin機構
- Router(pluginとして提供)
- など
- Wiki#Reference
いやー300行とはいえJSかぁ…
機能を最小限にしたやつを用意しました
- https://github.com/adwd/hyperapp
- Router, pluginなどを消して最小限に
- それでも200行になった程度ですが
tr;dr
- JSXからVirtualNodeを作る
- VirtualNodeからDOMを作成する
- actionで状態を変更する
- oldNode, nodeを比べて変わったとこだけDOMをいじる
JSXからVirtualNodeを作る
babelとhyperappのh関数がよしなにJSXをVirtualNodeに変換する様子
$ cd ./examples/helloworld
$ npm i
$ babel-node
> const { h } = require('hyperapp')
undefined
> <a href="/foo">hello</a>
{ tag: 'a', data: { href: '/foo' }, children: [ 'hello' ] }
> <ul><li>foo</li><li>bar</li></ul>
{ tag: 'ul',
data: {},
children:
[ { tag: 'li', data: {}, children: [Object] },
{ tag: 'li', data: {}, children: [Object] } ] }
VirtualNodeからDOMを作成する
root = document.body.appendChild(document.createElement("div"))
root.appendChild(createElementFrom(node))
function createElementFrom(node) {
var element
if (typeof node === "string") {
element = document.createTextNode(node)
} else {
element = document.createElement(node.tag)
for (var name in node.data) {
setElementData(element, name, node.data[name])
}
for (var i = 0; i < node.children.length; i++) {
element.appendChild(createElementFrom(node.children[i]))
}
}
return element
}
actionで状態を変更する
var result = action(model, data, actions)
model = merge(model, result)
render(model, view)
oldNode, nodeを比べて変わったとこだけDOMをいじる
if (shouldUpdate(node, oldNode)) {
if (typeof node === "string") {
element.textContent = node
} else {
parent.replaceChild(createElementFrom(node), element)
}
} else if (node.tag) {
updateElementData(element, node.data, oldNode.data)
var len = node.children.length, oldLen = oldNode.children.length
for (var i = 0; i < len || i < oldLen; i++) {
// childrenにも再帰的にpatchしていく
patch(element, oldNode.children[i], node.children[i], i)
}
}
- 意外?と素朴な実装だと思った
- ReactもReactElementだかで同じことやってる気がする
- Reactをわかった気持ちになった