了解 AMD:如何处理具有单向模块关系的响应流
Understanding AMD: how to handle response flow with a one-way module relationship
考虑一下,如果您愿意的话,可以考虑一个具有一些独特性的应用程序 views/states - 让我们称它为游戏。你有一个世界画面、一个战斗画面、一个多人游戏界面,也许还有一两个小游戏。
为了争论,每个视图之间没有很多共同的代码,所以它很适合 AMD - 一个中央 controller/dispatcher,每个游戏状态分成一个单独的 file/view.
dispatcher.core.js
> overworld.view.js
> battle.view.js
> tournament.view.js
> minigame.view.js
输入和键命令被路由到调度程序,并向下渗透到当前活动视图,后者又根据需要操纵 DOM。单向 AMD 关系,到目前为止一切顺利。
我被挂断的是响应流。通过系统的 API 响应数据是多种多样的,通常会同时影响多个视图。考虑这种情况:
- 用户按下按钮移动
- 键命令被路由到地图视图以进行移动动画
- 地图向服务器发送AJAX请求移动结果
- AJAX returns "battle commence" 响应调度员
- Dispatcher 告诉 map view 禁用它自己,然后 battle view 初始化
调度程序就是为此而设计的 - 接收指令和分发。这似乎是一个显而易见的选择,而不是让视图直接影响彼此。
但是,这里存在一个根本性缺陷 - 一旦 AJAX 结果从视图发送到调度程序,调度程序与视图之间的单向关系就被破坏了。您可以将调度程序用于 AJAX 回调,或者您可以指示调度程序为您进行 AJAX 调用 - 但无论哪种方式,视图都需要一种引用调度程序的方法,据我所知它,违背了AMD的核心宗旨。对于我的一生,我无法弄清楚如何正确实施!
我的问题是 - 如何正确实施这样的结构?这是 AMD 的局限性,还是我误解了它在更深层次上的用途?
此问题旨在针对更多一般情况,但如果它完全影响答案,我将分别对 AMD 和 AJAX 使用 Require 和 jQuery。
Is this a limitation of AMD, or am I misunderstanding it's use on a deeper level?
AMD 绝不会 强加 一般 对象实例之间的单向关系。 强烈建议避免的情况(因为即使这不是绝对要求)是循环依赖关系 模块之间 。对 AMD 重要的依赖类型是 loading dependencies.
你当然可以有一个名为 dispatcher
的模块:
define(function () {
function Dispatcher(views) {
this.views = views;
for (var ix = 0, view; (view = views[ix]); ++ix)
view.init(this);
}
return Dispatcher;
});
和viewA
、viewB
,它们的结构如下:
define(function () {
function View() {
// ...
}
View.prototype.init = function (dispatcher) {
this.dispatcher = dispatcher;
};
// Etc...
return View;
});
你的主模块可以做:
define(['dispatcher', 'viewA', 'viewB'], function (Dispatcher, ViewA, ViewB) {
var viewA = new ViewA();
var viewB = new ViewB();
var dispatcher = new Dispatcher([viewA, viewB]);
});
以上只是一个可能的示意图示例,并不是好的设计的处方。无论如何,关键是就 AMD 而言,在对象之间进行循环引用是完全可行的。
这里没有关于 AMD 的限制;这完全是关于模块本身的设计。
处理此问题的常用方法是使用 event-emitter。
dispatcher
可以直接在 view
上调用方法,但是 view
发出 dispatcher
可以监听和响应的事件,从而不需要循环引用(因为 view
不关心事件的去向,所以它不需要对 dispatcher
的引用。)
适合您的示例工作流程,它可能看起来像这样:
overworld
跟踪按键
overworld
响应按键的动画
overworld
为 dispatcher
发出 'move' 事件
// overworld.view
this.emit('move', {data});
// dispatcher
overworld.on('move', getMoveResult) // getMoveResult fires AJAX request
- 响应告诉
dispatcher
战斗时间到了
dispatcher
更新浏览量
overworld.hide()
battle.show()
考虑一下,如果您愿意的话,可以考虑一个具有一些独特性的应用程序 views/states - 让我们称它为游戏。你有一个世界画面、一个战斗画面、一个多人游戏界面,也许还有一两个小游戏。
为了争论,每个视图之间没有很多共同的代码,所以它很适合 AMD - 一个中央 controller/dispatcher,每个游戏状态分成一个单独的 file/view.
dispatcher.core.js
> overworld.view.js
> battle.view.js
> tournament.view.js
> minigame.view.js
输入和键命令被路由到调度程序,并向下渗透到当前活动视图,后者又根据需要操纵 DOM。单向 AMD 关系,到目前为止一切顺利。
我被挂断的是响应流。通过系统的 API 响应数据是多种多样的,通常会同时影响多个视图。考虑这种情况:
- 用户按下按钮移动
- 键命令被路由到地图视图以进行移动动画
- 地图向服务器发送AJAX请求移动结果
- AJAX returns "battle commence" 响应调度员
- Dispatcher 告诉 map view 禁用它自己,然后 battle view 初始化
调度程序就是为此而设计的 - 接收指令和分发。这似乎是一个显而易见的选择,而不是让视图直接影响彼此。
但是,这里存在一个根本性缺陷 - 一旦 AJAX 结果从视图发送到调度程序,调度程序与视图之间的单向关系就被破坏了。您可以将调度程序用于 AJAX 回调,或者您可以指示调度程序为您进行 AJAX 调用 - 但无论哪种方式,视图都需要一种引用调度程序的方法,据我所知它,违背了AMD的核心宗旨。对于我的一生,我无法弄清楚如何正确实施!
我的问题是 - 如何正确实施这样的结构?这是 AMD 的局限性,还是我误解了它在更深层次上的用途?
此问题旨在针对更多一般情况,但如果它完全影响答案,我将分别对 AMD 和 AJAX 使用 Require 和 jQuery。
Is this a limitation of AMD, or am I misunderstanding it's use on a deeper level?
AMD 绝不会 强加 一般 对象实例之间的单向关系。 强烈建议避免的情况(因为即使这不是绝对要求)是循环依赖关系 模块之间 。对 AMD 重要的依赖类型是 loading dependencies.
你当然可以有一个名为 dispatcher
的模块:
define(function () {
function Dispatcher(views) {
this.views = views;
for (var ix = 0, view; (view = views[ix]); ++ix)
view.init(this);
}
return Dispatcher;
});
和viewA
、viewB
,它们的结构如下:
define(function () {
function View() {
// ...
}
View.prototype.init = function (dispatcher) {
this.dispatcher = dispatcher;
};
// Etc...
return View;
});
你的主模块可以做:
define(['dispatcher', 'viewA', 'viewB'], function (Dispatcher, ViewA, ViewB) {
var viewA = new ViewA();
var viewB = new ViewB();
var dispatcher = new Dispatcher([viewA, viewB]);
});
以上只是一个可能的示意图示例,并不是好的设计的处方。无论如何,关键是就 AMD 而言,在对象之间进行循环引用是完全可行的。
这里没有关于 AMD 的限制;这完全是关于模块本身的设计。
处理此问题的常用方法是使用 event-emitter。
dispatcher
可以直接在 view
上调用方法,但是 view
发出 dispatcher
可以监听和响应的事件,从而不需要循环引用(因为 view
不关心事件的去向,所以它不需要对 dispatcher
的引用。)
适合您的示例工作流程,它可能看起来像这样:
overworld
跟踪按键overworld
响应按键的动画
发出 'move' 事件overworld
为dispatcher
// overworld.view this.emit('move', {data}); // dispatcher overworld.on('move', getMoveResult) // getMoveResult fires AJAX request
- 响应告诉
dispatcher
战斗时间到了 dispatcher
更新浏览量overworld.hide() battle.show()