[英]How to structure Javascript programs in complex web applications?
我有一个问题,不容易描述。 我正在编写一个 Web 应用程序,它充分利用了 jQuery 和 AJAX 调用。 现在我在 Javascript 架构方面没有很多经验,但我意识到我的程序没有一个好的结构。 我想我有太多的标识符指代相同的(至少或多或少)事物。
让我们看一下构成应用程序一小部分的任意示例 UI 小部件:小部件可能是窗口的一部分,窗口可能是窗口管理器的一部分:
现在我有四个不同的对象标识符用于同一件事,需要保持同步,直到重新加载页面。 这似乎不是一件好事。
编辑2:
我认为举个例子会很有帮助,尽管是人为的。 下面您会看到一个记录器小部件,可用于将块元素添加到显示已记录项目的网页。
makeLogger = function(){ var rootEl = document.createElement('div'); rootEl.innerHTML = 'Logged items:'; rootEl.setAttribute('class', 'logger'); var append = function(msg){ // append msg as a child of root element. var msgEl = document.createElement('div'); msgEl.innerHTML = msg; rootEl.appendChild(msgEl); }; return { getRootEl: function() {return rootEl;}, log : function(msg) {append(msg);} }; }; // Usage var logger = makeLogger(); var foo = document.getElementById('foo'); foo.appendChild(logger.getRootEl()); logger.log('What\\'s up?');
在这一点上,我有一个围绕 HTMLDivElement(托管对象)的包装器。 有了记录器实例(本机对象),我可以通过函数logger.getRootEl()轻松地使用它。
当我手头只有 DOM 元素并且需要对函数makeLogger返回的公共 API 执行某些操作时(例如在事件处理程序中),我会卡住。 这就是混乱开始的地方。 我需要将所有本机对象保存在存储库或其他东西中,以便我可以再次检索。 拥有从托管对象返回到我的本机对象的连接(例如对象属性)会更好。 我知道它可以做到,但它有一些缺点:
现在,我使用 jQuery 的 data() 方法进行反向引用。 但总而言之,我不喜欢我必须跟踪托管对象与其本机对应对象之间关系的方式。
您可以使用全局注册表:
window.WidgetRegistry = {};
window.WidgetRegistry['foowidget'] = new Widget('#myID');
当 AJAX 调用返回时,他们可以像这样获得小部件:
var widgetID = data.widgetID;
if (widgetID in window.WidgetRegistry) {
var widget = window.WidgetRegistry[widgetID];
}
对于您的 jQuery 调用:我猜它们相对便宜,因为 jQuery 缓存对象供以后使用。 但是您可以使用.data()
扩展上述建议的WidgetRegistry
:
var $widget = $('#myWidget');
var widgetID = 'foo';
$widget.data('widget', widgetID);
通过这种方式,您可以存储附加到每个 jQuery 对象的小部件 ID,并从全局注册表重新访问它。
测试,如果一个 jQuery 对象有一个现有的小部件:
return $('#test').data('widget') &&
($('#test').data('widget') in window.WidgetRegistry);
请注意,这些只是建议。 实际上,有很多方法可以实现这样的整合。 如果您想将您的代码与 jQuery 更深入地结合,您可以扩展 jQuery object ,以便您可以编写如下内容:
$('#widget').widget({'foo':'bar'});
// and/or
var allWidgets = $('*:widget');
// ...
对于需要同步的四个对象,您可以拥有一个对象并在构造函数中传递引用,或作为函数参数传递。
我解决这个问题的方法是永远不会丢失对包装器对象的引用。 每当需要一个 DOM 对象时(例如插入到页面中),这个包装器对象就会提供它。 但是将该小部件粘贴到屏幕上,包装器对象设置特定于小部件的所有事件处理和 AJAX 处理代码,因此在这些事件处理程序和 AJAX 回调中始终维护包装器的引用。
我已经使用 MooTools 在 jsfiddle 上创建了一个简单的示例,这可能对您有意义。
我不确定我是否完全理解你的问题,但我会尝试指出一些想法。
在我看来,您应该创建基本小部件类,其中包含小部件的通用功能。
让我们以 AppName.Widgets.base() 为例。 实例变量之一是 _events,它是将事件存储为键并将函数存储为值的对象。 这样每个类都定义了这个小部件的事件,你可以很容易地在构造函数中绑定它们。 至于字符串标识符,最简单的方法是使用 toString()。
例子:
namespace('AppName.Widgets'); // you can find implementations easy
AppName.Widgets.base = function() {
if (!this._type) return;
this._dom = $('div.widget.'+this._type);
for (var e in this._events) {
this._dom.bind(e, this._events[e]);
}
this.toString = function() { return this._type; };
}
AppName.Widgets.example = function() { // extends AppName.Widgets.base
this._type = 'example';
this._events = { 'click' : function(e) { alert('click'); } };
AppName.Widgets.base.call(this);
}
您能做或不能做的很多事情取决于您对 javascript 的控制程度。 就我个人而言,我经常不得不使用其他人构建的库,因此我可能只能使用 DOM 节点,但我确实需要我的对象。 在这些情况下,我发现使用 jQuery 中的数据功能非常方便。 通过使用数据功能,您可以将您的对象“存储”在 DOM 节点内,以便以后检索。
鉴于您上面的示例,以下是您在拥有仅使用 DOM 节点的函数后如何使用数据功能取回小部件的方法。
makeLogger = function(){
var rootEl = document.createElement('div');
rootEl.innerHTML = 'Logged items:';
rootEl.setAttribute('class', 'logger');
var append = function(msg){
// append msg as a child of root element.
var msgEl = document.createElement('div');
msgEl.innerHTML = msg;
rootEl.appendChild(msgEl);
};
var self = {
getRootEl: function() {return rootEl;},
log : function(msg) {append(msg);}
};
// Save a copy to the domNode
$(rootEl).data("logger", self);
return self;
};
// Example of only getting the dom node
function whatsUp (domNode){
// Get the logger from the domNode
$(domNode).data('logger').log('What\'s up?');
}
// Usage
var logger = makeLogger();
var loggerNode = logger.getRootEl();
var foo = document.getElementById('foo');
foo.appendChild(loggerNode);
whatsUp(loggerNode);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.