Hyperapp是一个非常小的微框架,用于构建声明式Web应用程序,大小只有1kB,API类似于React的,完美,对吗?我们将构建一个小型计数应用程序来演示Hiperapp的功能。
新的超级项目
要开始,我们可以创建一个新的 Node 应用程序并安装 hyperapp
. 然后我们将使用 parcel服务这个应用程序:
1# New directory, here we call it `hyper`
2$ mkdir hyper && cd hyper
3
4# Initialise Node project
5$ npm init -y
6
7# Install Hyperapp
8$ npm i hyperapp
9
10# Create index.html and app.js
11$ touch index.html
12$ touch app.js
13
14# Install `parcel` globally
15$ npm i parcel -g
16
17# Serve our application in the browser
18$ parcel index.html
然后,我们可以编写一个标准的 index.html 页面,其中包括 app.js,其中包含我们的 hyperapp 代码。
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <title>🎉 Hyperapp</title>
5</head>
6<body>
7 <div id="app"></div>
8 <script src="app.js"></script>
9</body>
10</html>
构建一个计数
国家驱动的应用程序总是从一个Counter例子开始,这使我们能够习惯于我们的应用程序中的数据流。
1[label app.js]
2const state = {
3 count: 0
4}
然后我们可以根据该状态
定义一个视图
,可以使用标准模板语法来显示:
1[label app.js]
2// ...
3const view = state => (
4 <div>
5 <h1>{state.count}</h1>
6 </div>
7);
最后,我们可以将此附加到DOM内部的某个特定的元素中,我已经选择将此附加到一个div
中,其中有app
的id
:
1[label app.js]
2// ...
3const el = document.getElementById('app');
4
5const main = app(state, {}, view, el);
以下是我们的简单应用程序的样子:
由于状态
是不可变的,不应该直接更新,我们现在可以添加行动
来操纵我们的状态
:
1[label app.js]
2// ...
3const actions = {
4 increment: () => state => ({ count: (state.count += 1) }),
5 decrement: () => state => ({ count: (state.count -= 1) })
6};
这可以连接到我们的主要
和观点
,让它可以访问我们的行动
:
1[label app.js]
2// ...
3const view = (state, actions) => (
4 <div>
5 <h1>{state.count}</h1>
6 <button onclick={() => actions.increment()}>Increment</button>
7 <button onclick={() => actions.decrement()}>Decrement</button>
8 </div>
9);
10
11const main = app(state, actions, view, el);
现在,如果我们选择增加或减少,我们会看到总数上升或下降。
如果我们想让这个数字上升或下降?让我们添加这个功能。
首先,我们可以将一个新项目添加到我们的状态
对象中,我选择称之为diff
,因为这代表了添加或扣除的区别:
1const state = {
2 count: 1,
3 diff: 1
4};
然后,我们可以根据以下情况将我们的行动
更新为增加或减少:
1const actions = {
2 updateCount: diff => state => ({ diff: diff }),
3 increment: diff => state => ({ count: (state.count += Number(diff)) }),
4 decrement: diff => state => ({ count: (state.count -= Number(diff)) })
5};
最后,我们可以更新我们的观点
:
1const view = (state, actions) => (
2 <div>
3 <input value={state.diff} oninput={e => actions.updateCount(e.target.value)} />
4
5 <h1>{state.count}</h1>
6 <button onclick={() => actions.increment(state.diff)}>Increment</button>
7 <button onclick={() => actions.decrement(state.diff)}>Decrement</button>
8 </div>
9);
现在我们有能力利用输入数据来更新我们的状态。
组件
现在让我们看看我们如何从我们的Hyperapp项目中制作组件,我们将创建一个计数
组件,并看看我们如何将其嵌入到页面和路线中。
在components/Count.js
创建一个新文件,并添加一个计数器,从props
中输入一个计数器
:
1[label Count.js]
2import { h } from 'hyperapp';
3
4const Count = ({ count }) => <h1>{count}</h1>;
5
6export default Count;
然后我们可以在app.js 中导入
:
1[label app.js]
2import Count from './components/Count';
3
4// ...
然后,我们可以将计数
作为附件转移到计数
中的视图
:
1[label app.js]
2// ...
3const view = () => (state, actions) => (
4 <div>
5 <Count count={state.count} />
6 <button onclick={actions.increment}>Increment</button>
7 <button onclick={actions.decrement}>Decrement</button>
8 </div>
9);
我还更新了我们的状态
和行动
为简单的增加
和减少
的计数
:
1const state = {
2 count: 0
3};
4
5const actions = {
6 increment: () => ({ count: (state.count += 1) }),
7 decrement: () => ({ count: (state.count -= 1) })
8};
路线
我们还可以利用 Hyperapp 内部的路由器,让我们像这样安装路由器包(@hyperapp/router
):
1$ npm i @hyperapp/router
然后我们可以导入``app.js
内部的路由组件:
1[label app.js]
2import { Link, Route, location } from '@hyperapp/router';
现在我们可以创建两个不同的页面,主页
和博客
:
1[label app.js]
2// ...
3const Home = () => (state, actions) => (
4 <div>
5 <Count count={state.count} />
6 <button onclick={actions.increment}>Increment</button>
7 <button onclick={actions.decrement}>Decrement</button>
8 </div>
9);
10
11const Blog = () => <h1>Blog!</h1>;
主页
页面包含我们以前的对比示例,而博客
页面只是一些文本,让我们将这些分配为路线
和链接
,并在视图
中:
1[label app.js]
2// ...
3const view = state => (
4 <div>
5 <ul>
6 <li>
7 <Link to="/">Home</Link>
8 </li>
9 <li>
10 <Link to="/blog">Blog</Link>
11 </li>
12 </ul>
13
14 <Route path="/" render={Home} />
15 <Route path="/blog" render={Blog} />
16 </div>
17);
接下来,我们需要让路由器访问位置
,因为它是基于历史API。
1[label app.js]
2const state = {
3 location: location.state,
4 count: 0
5};
6
7const actions = {
8 location: location.actions,
9 increment: () => state => ({ count: (state.count += 1) }),
10 decrement: diff => state => ({ count: (state.count -= 1) })
11};
最后,我们需要订阅位置
本身:
1[label app.js]
2// ...
3const unsubscribe = location.subscribe(main.location);
现在我们能够在我们的应用程序内部选择不同的页面!
以下是路由示例的完整代码:
1[label app.js]
2import { h, app } from 'hyperapp';
3import { Link, location } from '@hyperapp/router';
4
5import Count from './components/Count';
6
7const state = {
8 location: location.state,
9 count: 0
10};
11
12const actions = {
13 location: location.actions,
14 increment: () => state => ({ count: (state.count += 1) }),
15 decrement: diff => state => ({ count: (state.count -= 1) })
16};
17
18const Home = () => (state, actions) => (
19 <div>
20 <Count count={state.count} />
21 <button onclick={actions.increment}>Increment</button>
22 <button onclick={actions.decrement}>Decrement</button>
23 </div>
24);
25
26const Blog = () => <h1>Blog!</h1>;
27
28const view = state => (
29 <div>
30 <ul>
31 <li>
32 <Link to="/">Home</Link>
33 </li>
34 <li>
35 <Link to="/blog">Blog</Link>
36 </li>
37 </ul>
38
39 <Route path="/" render={Home} />
40 <Route path="/blog" render={Blog} />
41 </div>
42);
43
44const main = app(state, actions, view, document.body);
45
46const unsubscribe = location.subscribe(main.location);
结论
有了這一點,你應該離開賽車! 此外,也留意 Hyperapp 2.0,這應該很快就會出售。