概述
本篇教程属于宜搭的高级使用教程,我们会参考 TodoMVC 的功能,从零开始搭建一个简易的 TodoMVC 页面,最终的效果如下所示(用户也可以通过访问 示例页面 查看效果或 启用该应用试玩 ):
- 宜搭基本组件使用;
- 用户行为事件处理;
- 全局变量使用;
- 条件渲染&循环渲染使用;
- 自定义样式;
- 远程 API 使用;
- 宜搭 OpenAPI 使用;
基础 UI——赋予 TodoMVC 基础的骨架
首先我们拆解一下官方 TodoMVC 页面的结构,大致会包含以下内容:
- 大 Logo- 一个静态文案,可以使用
文本组件; - 任务输入框- 用于输入新增任务的内容,可以使用
输入框组件; - 任务状态控制器- 用于控制所属任务的完成状态,可以使用
单选组件; - 任务内容展示- 用于展示所属任务的具体内容,可以使用
文本组件; - 任务操作项- 用于删除所属任务,可以使用
按钮组件(图中用的是一个 Icon,为了更好的表达语义,我们选择使用按钮组件); - 待办任务数量展示- 用于展示待完成的任务总数,可以使用
文本组件; - 任务状态筛选器- 用于筛选不同状态的任务列表,可以使用
单选组件; - 清除所有完成状态的任务- 用于清除所有已完成的任务,可以使用
按钮组件; - 工具介绍- 介绍 Todos 的一些基本信息,静态展示,可以使用
文本组件;

逻辑实现——赋予 TodoMVC 有趣的灵魂
上一步我们完成了 TodoMVC 的基本 UI 搭建,相当于完成了一副骨架,接下来我们就要赋予它灵魂了,这也是最重要的一步,在这之前我们先分析一下 TodoMVC 大概有哪些功能:- 创建待办任务- 用户在任务输入框中填写任务内容,点击回车键,会在任务列表中创建一条待完成的任务;
- 更新状态- 用户通过点击任务控制器,更改当前任务的完成状态(若已完成,任务文案将会被横线划掉);
- 编辑任务内容- 用户点击任务
Edit按钮,唤起任务内容输入框,用户可以进行内容更改并点击回车键进行提交(官方的 TodoMVC 是采用双击的方式切换任务编辑状态,由于宜搭暂未开放双击事件,因此使用按钮方式进行编辑态切换); - 删除待办任务- 用户点击
Delete按钮,删除指定的待办任务; - 统计未完成数量- 在页面左下角显示当前未完成的待办任务的统计数量;
- 根据状态筛选- 用户通过切换页面底部的筛选器显示不同的任务列表:
- All - 显示所有任务列表;
- Active - 显示待完成的任务列表;
- Completed - 显示已完成的任务列表;
- 清空完成状态的待办任务- 用户通过点击页面右下角的
Clear completed按钮批量删除所有已完成的任务;

步骤 1:创建页面全局变量
宜搭的设计理念和 React 类似都是 MVVM 模式,因此我们在进行功能开发前需要先定义页面中需要使用的数据模型,即全局状态(具体使用详见 页面状态文档),我们定义以下全局变量:
- todoList(数组类型) - 用于记录列表中的所有待办任务信息,结构如下所示:
- editRowId(数字类型)- 用于标记当前处于编辑状态的任务 ID(当处于编辑状态时,任务内容区域会变为输入框供用户进行内容修改),默认可以不填;
- mode(字符串类型)- 任务列表当前筛选状态值,可选值:All、Active 和 Completed,默认为:
"All";P.S.: 需要引号。 - newId(数字类型)- 一个新 ID 生成标识,每当创建一个新的任务,会用这个
newId作为新的任务 ID,同时newId也会自动加 1,保证所有任务 ID 的唯一性;
步骤 2:实现创建待办任务功能
现在我们就可以开始实现第一个功能了,在这一步我们希望用户再输入框中输入任务内容,并点击回车键创建一条待办任务(事件绑定使用详见 事件处理文档)。
- 给输入框添加一个唯一标识(“input”),方便获取获取输入框输入的内容;
- 给输入框绑定一个 onKeyDown 的事件 (“onRowAdd”),当用户点击键盘时触发处理;
- 实现
onRowAdd的方法,判断如果用户点击的是回车键,则在 todoList 的最上方插入一条新的待办任务数据;
步骤 3:实现任务列表循环展示
通过上一步,我们已经可以将用户输入的内容生成待办任务并添加到全局变量 todoList 中,接下来我们就需要在输入框的下方展示待办列表了,这一步会用到 循环渲染 的相关知识。- 首先选中待办任务信息外层的容器组件,然后在高级属性中通过变量绑定方式将 state.todoList 绑定到容器的循环数据上;


-
其次我们需要将任务的完成状态及内容绑定到循环数据对应的数据字段上,在循环渲染中,开发者可以通过
item拿到当前行的数据,如下所示:-
设置单选组件的默认值属性绑定任务状态;
-
设置文本组件的内容属性绑定任务内容;
-
设置单选组件的默认值属性绑定任务状态;

步骤 4:实现任务更新&删除
通过上一步,我们基本实现了 todoList 的添加及展示功能,接下来我们就需要操作实现任务的更新及删除功能了。实现任务内容的多态展示
通过分析 TodoMVC 的功能,我们发现任务列表中的任务内容有 3 种不同的展示形态:

- 编辑态- 当全局变量 editRowId 和当前行的 ID 一致时显示:
state.editRowId === this.item.id - 待完成态- 当前任务不处于编辑态且其状态为待完成时显示:
state.editRowId !== this.item.id && !this.item.done - 已完成态- 当前任务不处于编辑态且其状态为已完成时显示:
state.editRowId !== this.item.id && this.item.done
实现任务内容编辑
在这一步我们希望实现的效果是,用户点击任务操作栏中的Edit 按钮,任务切换到编辑态,同时用户可以基于现有内容进行 2 次编辑,并点击回车提交编辑内容,任务回归到之前的展示状态。
要实现该功能,我们需要分两步进行:
- 给 Edit 按钮绑定 onClick 事件 (“onEdit”),当用户点击
Edit按钮时,我们将当前任务的 id 设置为全局变量 RowEditId 并触发重新渲染使该任务状态切换为编辑态,如下所示:
- 给任务编辑态的输入框组件设置一个唯一标识 (“rowInput”),并参考创建任务的实现逻辑,绑定 onKeyDown 事件 (“onRowEdit”) 更新 todoList 中当前任务的内容,如下所示:

item.content 。
实现任务完成状态切换
当用户点击任务最前方的单选框时,需要能够切换任务的完成状态,因此我们需要监听单选组件的onChange 事件 (“onTodoCheck”),当单选框状态改变时实时更改 todoList 中任务状态:

实现任务删除
当用户点击任务操作项中的Delete 按钮时,需要能够从任务列表中将该任务删除,这一步相对简单,我们只需要绑定 Delete 按钮的 onClicks 事件 (“onDelete”),将该条任务从 todoList 中去掉:

步骤 5:实现待办任务统计展示
在 todoMVC 页面中,在左下角会有一个统计信息用于统计当前任务列表中待完成的任务数量,这个功能就相对比较简单,我们只需要给文本组件绑定一个变量在每次渲染时计算并显示 todoList 中 done 状态为 false 的数量,如下所示:
步骤 6:实现待办任务按状态筛选
在这一步,我们要实现任务列表的状态筛选功能,当用户点击底部的状态筛选器时,筛选不同状态的任务列表,这个功能我们需要分 2 步实现:- 监听单选组件的 onChange 事件 (“onModeChange”),当用户点击更改筛选器时更新全局变量 mode 的值:

- 大家还记得我们在步骤3中给任务列表绑定的循环数据吧,在这一步我们需要重新绑定任务列表的循环数据,将之前的
state.todoList改为getShowList()用于根据当前筛选值返回符合条件的任务列表:

步骤 7:实现清除完成状态待办任务
这一步就比较简单了,它其实是步骤 4 中删除任务的批量操作,当用户点击Clear Completed 按钮时,将任务列表中的已完成状态任务清除掉,我们只需要监听 Clear Completed 按钮的 onClick 事件 (“onClearCompleted”),当用户点击按钮时清除掉 todoList 中所有已完成的任务:

步骤 8:实现本地存储
通过上面的实现,我们基本实现了 todoMVC 的核心功能,但是我们的数据目前是存在 state 上的,每次刷新都会清除掉,因此需要找一个地方将用户的 todoList 存起来,以便下次进入的时候能够显示,我们选择通过 localStorage 进行本地存储,因此我们实现了以下两个方法:- saveTodoData - 将 state 中的 todoList 和 newId 存储到 localStorage;
- getTodoData - 从 localeStorage 中获取数据;
- 在 didMount 生命周期中调用 getTodoData API 获取本地数据并更新 state 中的状态;
- 在上面所有修改 todoList 的
setState语句下方增加this.saveTodoData()调用;


样式完善——赋予 TodoMVC 好看的皮囊
上一步我们完成的 TodoMVC 的基础功能实现,但是给人的第一感觉就是丑,对于一个产品好看的皮囊不是最重要的,但是如果没有一副好的皮囊,它一定不是一个好的产品,宜搭提供了自定义样式能力,详见 样式定制文档,通过定制样式,我们将得到下效果,(用户也可以通过访问 示例页面 进行试用,通过 设计器 查看具体实现):
-
通过样式面板定制组件样式 - 例如 TodoList 的大 logo,我们可以通过在样式面板中配置字体样式来实现,如下所示:
-
通过 CSS 定制组件样式 - 样式面板覆盖面毕竟有限,宜搭提供了通过书写 CSS 来定制组件样式的能力,例如面板下方的层叠效果,我们就是通过 before 伪类的方式实现的:
数据源对接——赋予 TodoMVC 强大的装备
到上一步,官方的 TodoMVC 的基本能力就实现了,不过细心的开发者会发现现在这个产品状态功能可用,但是如果换一个浏览器访问待办任务数据会丢失不见,这可不是一个可以交付的 TodoList 功能,宜搭除了提供基础的 UI 及前端逻辑定制能力,还提供了强大的数据能力,现在就让我们为 TodoMVC 披上强大的装备,实现数据的持久化存储吧,最终达到的效果如下所示:
步骤 1:创建普通表单,构建任务数据存储结构
首先,我们在当前应用下创建一个普通表单用于存储待办任务的信息,具体创建普通表单的方案详见 宜搭使用文档,这里不展开描述,如下所示:
步骤 2:使用远程 API,实现任务的增删改查
有了存储任务数据的地方,我们就需要在 TodoMVC 中创建异步 API 用户实现任务的增删改查,宜搭设计器提供了远程 API 配置用于请求远程 http 接口(详见 远程 API 文档),同时宜搭也提供了常用的 OpenAPI 接口来实现数据的基本操作(详见 OpenAPI 文档),我们在搭建页面中创建以下远程 API 配置:
todoList
获取任务列表数据,具体配置如下所示:
- 将自动加载开关打开,即,进入页面自动加载该接口,宜搭会自动将接口返回内容挂载到
state.todoList上; - 按照 OpenAPI 文档中的格式配置请求链接及请求方法;
- 配置请求参数
- formUuid- 我们用于存储任务信息的表单的唯一标识,即上一步创建的表单的 formUuid;
- currentPage- 当前页码,宜搭的开放接口是支持分页的,我们这里主要做示例展示就不做分页处理了,将参数固定设置为 1 取第一页数据;
- pageSize- 每页数量,宜搭开放接口默认每页数量为 10,为了更好的演示体验,我们将参数固定设置为 50,取近 50 条数据;
- 配置 didFetch 处理函数,将开放接口的结构转换成我们在上面逻辑实现部分能够被识别的数据结构,具体实现如下:
add
用于创建待办任务,具体配置如下:
- 按照 OpenAPI 文档中的格式配置请求链接及请求方法;
- 配置请求参数
- formUuid- 同上;
- appType- 当前应用的应用 ID (以 APP_开头,可以从当前页面 URL 地址上获取);
- 配置 willFetch 处理函数,我们需要按照 OpenAPI 中的参数格式将任务信息转换成可以被宜搭识别的格式,具体实现如下:
- 配置 didFetch 处理函数,当创建接口请求成功后弹窗提醒操作成功并刷新任务列表,具体实现如下:
del
用于删除待办任务,相对简单,具体配置如下:
- 删除任务的任务 ID 不固定,因此需要再调用
dataSourceMap.del.loadAPI 是进行传入具体要删除的任务 ID; - 删除任务没有实现 didFetch,是因为在实现 Clear Completed 时需要批量调用该异步 API,所以需要在手动调用异步请求的 then 方法中实现 didFetch 的逻辑;
update
用于更新待办任务,具体配置如下:
- 按照 OpenAPI 文档中的格式配置请求链接及请求方法;
- 配置 willFetch 处理函数,同 add 配置,我们需要把任务内容转换为宜搭可以识别的数据结构,具体实现如下:
- 配置 didFetch 处理函数,逻辑与 add 相似成功提示+刷新列表,具体实现如下:
步骤 3:采用远程 API 调用替换原有数据管理逻辑
完成了上一步的远程 API 设置,我们仅需要修改之前部分的逻辑实现部分,将操作全局变量的逻辑替换为远程 API 调用,具体改动点如下:- 修改onRowAdd实现,调用 add 远程 API 进行任务创建,具体实现如下:
- 修改onDelete实现,调用 del 远程 API 进行任务删除,具体实现如下:
- 修改onRuleEdit实现,调用 update 远程 API 更新任务内容,具体实现如下:
- 修改onTodoCheck实现,调用 update 远程 API 更新任务状态,具体实现如下:
- 修改onClearCompleted实现,批量调用 del 远程 API 清除已完成任务,具体实现如下: