CHACHA 和 HTML 模板
框架提供了它们自己表达可观察列表的方式。现在很多开发者也依赖提供这种功能的非框架库,如 MobX。
通用的可观察列表的主要问题在于它们是通用的。这以性能为代价增加了便利性,而且还需要特殊的开发者工具来调试那些库在后台做的复杂动作。
使用这些库并理解它们的作用是可以的,无论选择什么样的 UI 框架,它们都是有用的,但使用替代方案可能不会更复杂,而且可以避免一些在你试图推出自己的模型时产生的陷阱。
变化通道(或 CHACHA)
CHACHA——也被称为变化通道(Changes Channel)——是一个双向流,其目的是通知意图方向和观察方向的变化。
- 在意图方向上,UI 将用户意图的变化通知给模型。
- 在观察方向上,模型将对模型所做的改变通知给 UI,而这些改变需要显示给用户。
这也许是一个有趣的名字,但它不是一个复杂或新颖的模式。双向流在 Web 和软件中随处可见(例如,MessagePort)。在这种情况下,我们正在创建一个双向流,它有一个特殊的目的:向 UI 报告实际的模型变化,并向模型报告意图。
CHACHA 的接口通常可以从应用的规范中导出,而不需要任何 UI 代码。
例如,一个允许你添加和删除联系人并从服务器加载初始列表的应用程序(带有刷新选项)可以有一个 CHACHA,它看起来像这样:
interface Contact {
id: string;
name: string;
email: string;
}
// "Observe" Direction
interface ContactListModelObserver {
onAdd(contact: Contact);
onRemove(contact: Contact);
onUpdate(contact: Contact);
}
// "Intent" Direction
interface ContactListModel {
add(contact: Contact);
remove(contact: Contact);
reloadFromServer();
}
请注意,这两个接口中的所有函数都是无效的,只接收普通对象。这是故意的。CHACHA 被构建成一个通道,有两个端口来发送消息,这使得它可以在 EventSource、HTML MessageChannel、服务工作者或任何其他协议中工作。
CHACHA 的好处是,它们很容易测试。你发送动作并期待对观察者的特定调用作为回报。
列表项的 HTML 模板元素
HTML 模板是存在于 DOM 中的特殊元素,但不会被显示。它们的目的是生成动态元素。
当我们使用 template 元素时,我们可以避免在 JavaScript 中创建元素和填充它们的所有模板代码。
下面将使用 template 为列表添加名称:
<ul id="names">
<template>
<li><label class="name" /><span class="hljs-name"li>
<span class="hljs-name"template>
<span class="hljs-name"ul>
<script>
function addName(name) {
const list = document.querySelector('#names');
const item = list.querySelector('template').content.cloneNode(true).firstElementChild;
item.querySelector('label').innerText = name;
list.appendChild(item);
}
class="hljs-name"script>
通过使用列表项的 template 元素,我们可以在原始 HTML 中看到列表项——它不是用 JSX 或其他语言“渲染”的。你的 HTML 文件现在包含了应用程序的所有 HTML——静态部分是渲染的 DOM 的一部分,而动态部分在模板中表达,准备在时机成熟时被克隆并追加到文档中。
集大成者:TodoMVC
TodoMVC 是一个 TODO 列表的应用规范,用于展示不同的框架。TodoMVC 模板带有现成的 HTML 和 CSS,帮助你专注于框架。
你可以在 GitHub 资源库中使用这个结果,并且可以获得完整的源代码。
从规范派生的 CHACHA 开始
我们将从规范开始,并使用它来构建 CHACHA 接口:
interface Task {
title: string;
completed: boolean;
}
interface TaskModelObserver {
onAdd(key: number, value: Task);
onUpdate(key: number, value: Task);
onRemove(key: number);
onCountChange(count: {active: number, completed: number});
}
interface TaskModel {
constructor(observer: TaskModelObserver);
createTask(task: Task): void;
updateTask(key: number, task: Task): void;
deleteTask(key: number): void;
clearCompleted(): void;
markAll(completed: boolean): void;
}
任务模型中的函数直接来自规范和用户可以做的事情(清除已完成的任务,将所有任务标记为已完成或正在进行,获得正在进行和已完成的计数)。
请注意,它遵循 CHACHA 的准则。
- 有两个界面,一个是动作的,一个是观察的。
- 所有的参数类型都是基元或普通对象(很容易翻译成 JSON)。
- 所有的函数都返回 void。
TodoMVC 的实现使用 localStorage 作为后端。
该模型非常简单,与关于 UI 框架的讨论没有多大关系。它在需要的时候保存到 localStorage,并在某些情况发生变化时向观察者触发回调,这些变化可能是用户操作的结果,也可能是模型第一次从 localStorage 加载的时候。
精简的、面向表单的 HTML
接下来,我将采用 TodoMVC 模板,并将其修改为面向表单的模板:表单的层次结构,输入和输出元素代表可以用 JavaScript 改变的数据。
我怎么知道某个东西是否需要成为表单元素?作为一个经验法则,如果它与模型中的数据绑定,那么它就应该是一个表单元素。
完整的 HTML 文件是可用的,但这里是其主要部分:
class="todoapp">
<header class="header">
todosclass="hljs-name"h1>
-
Web
+关注
关注
2文章
1262浏览量
69446 -
框架
+关注
关注
0文章
403浏览量
17477 -
编程
+关注
关注
88文章
3614浏览量
93698
发布评论请先 登录
相关推荐
评论