我也想知道。
起点:
- Famo.us 应用采用分层渲染树结构。
- Famo.us 使用 RequireJS 模块。
- Famo.us 鼓励使用视图
- 视图是可重用的组件。
- 视图封装了功能(以及渲染树的一部分)。
- 视图使用事件进行通信。
- 视图由 options 对象初始化。
我们需要添加结构来管理:
- 业务逻辑
- REST api 调用、音频、本地存储等非 UI 功能。
- 数据流:
模块、事件和松散耦合
目标:
- 可扩展的应用程序结构。
- 小型、独立、可测试的模块。
- 促进代码的重用。
- 轻松共享模块(只需将文件拖放到您的项目中)
-
不依赖其他 Javascript 框架。
通常,模块之间存在紧密耦合:
- 模块实例化并保持对另一个模块的引用
- 模块跨越了它们的语义边界
- 了解特定 REST api 的视图
- 使用特定模型代码的视图,例如 Backbone(参见 Taasky 示例)
以下是如何避免这种情况:
- 当发生有趣的事情(用户输入、数据更改等)时,模块会发出事件
-
Mediator 监听事件
- Mediator 调用另一个模块的公共 API。
因此,它不会让 View-module 知道 Backbone 模型的内容,而是发出这样的用户输入事件:
- Todo-Edit-View 发出“用户输入”事件:
"todo-update",{id:1,title:'Code'}
- Todo-Mediator 监听事件。
- Todo-Mediator 调用 Todo 模块的
tasks.find({id:1}).update({title:'Code'})(例如使用 Backbone)
规则
- 有模块和中介
- 模块具有公共 API 并发出事件
- API 和事件保持在其语义边界内;即“updateTitle()”而不是“onBackboneModelChange()”
- Mediator 将应用程序耦合起来,即骨干模型更改与待办事项标题更新。
- 中介是单身人士
- 一个应用程序可以有多个中介。
例如,像 GMail 这样的电子邮件应用程序可以有中介用于
- 聊天功能
- 待办事项列表功能
- 阅读电子邮件
- 撰写和发送电子邮件
文件结构:
/src
/lib
/services
/mediators
/layout
/content
/config
/main.js
模块类型:
-
Service:独立模块,封装非UI功能如:LocalStorage、Ajax、Web Audio等。
-
布局:内容的布局、动画和定位,例如:ScrollView、HeaderFooterView等
-
内容:应用的实际内容:渲染树的叶节点(表面)。
注意布局和内容之间的区别。通过将 UI 组件和布局与实际内容分离,可以轻松重用 UI 模式,例如侧面板、弹出窗口、导航栏、粘性标题、滚动视图等。
另外,我建议为每个仅包含 结构 和最少主题的 Layout 创建一个 *.css。所有主题都可以在config/theme.css 中覆盖/扩展,因此很容易重新设计应用程序。
其他代码:
-
Mediator:使用事件和公共 API 耦合模块。
-
配置:包含整个应用程序在初始化时使用的所有选项。
-
main.js:引导应用程序。
Main.js 中的引导应用程序
- 创建中介
- 创建服务
- 构造渲染树
模块生命周期
当一个模块被创建时,它会向所有中介宣布它的存在。我们使用 Famo.us Engine 来发出一个全局事件。这将是唯一必需的依赖项!
Engine.trigger('created',this)
当一个模块被销毁时,它会向所有中介宣布它的销毁。
Engine.trigger('destroyed',this)
中介器监听created 和destroyed 事件并将模块粘合在一起:
var someDataModule; // Backbone or whatever
Engine.on('create',function(module){
if(module instance of SomeDataModule) {
var someDataModule = module;
}
if(module instanceof TodoView) {
module.on('change-title',function({id:id,title:title}){
someDataModule.find({id:id}).set('title',title);
})
}
})
在简单的中介中,您可以按顺序初始化模块(即SomeDataModule 在TodoView 之前)。但是,在更复杂的场景中,您可能需要使用 Promise 来耦合所有内容。
依赖关系说明
“自包含”模块有三个例外:
Layout 和 Content 模块允许具有分层依赖关系。父母可以初始化孩子,并期待这些孩子的某些事件。 ListView 可能会初始化 ItemView 并处理 ItemRemoved 事件。
Services 可以是另一个服务的 Facade/Adapter。例如,DataService 可能为RestApiService 提供简化、抽象和安全层。
当然,中介具有硬连接的依赖关系,因为它们耦合了应用程序!