如何在复杂的web应用程序中构造Javascript程序?
我有一个问题,很难描述。我正在编写一个web应用程序,它充分利用了jQuery和AJAX调用。现在我在Javascript架构方面没有太多经验,但我意识到我的程序没有很好的结构。我想我有太多的标识符指向同一件事(至少或多或少) 让我们看一看构成应用程序一小部分的任意示例性UI小部件:小部件可能是窗口的一部分,窗口可能是窗口管理器的一部分:如何在复杂的web应用程序中构造Javascript程序?,javascript,architecture,web-applications,Javascript,Architecture,Web Applications,我有一个问题,很难描述。我正在编写一个web应用程序,它充分利用了jQuery和AJAX调用。现在我在Javascript架构方面没有太多经验,但我意识到我的程序没有很好的结构。我想我有太多的标识符指向同一件事(至少或多或少) 让我们看一看构成应用程序一小部分的任意示例性UI小部件:小部件可能是窗口的一部分,窗口可能是窗口管理器的一部分: EventHandler使用DOM元素作为参数。DOM元素表示浏览器中的小部件 很多时候,我使用jQuery对象(基本上是DOM元素的包装器)来处理小部件。有
编辑:
*@Will Morgan*:这是一个允许在浏览器中创建web表单的表单设计器。后端是Zope,一个python web应用服务器。很难更加明确,因为这是我在使用trio jQuery、DOM树和我自己的原型类实例进行Javascript开发时经常遇到的一个问题。 编辑2:
我认为举个例子会有帮助,尽管是人为的。下面是一个logger小部件,可用于将块元素添加到显示日志项的网页中
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?');
在这一点上,我有一个围绕HtmlLevel(托管对象)的包装器。有了logger实例(本机对象),我可以通过logger.getRootEl()函数轻松地使用它。当我手头只有DOM元素,需要使用函数makeLogger返回的公共API(例如,在事件处理程序中)执行某些操作时,我陷入了困境。这就是混乱开始的地方。我需要将所有本机对象保存在存储库或其他地方,以便再次检索。如果有一个从托管对象到本机对象的连接(例如对象属性),那就更好了。 我知道这是可以做到的,但它有一些缺点:
- 这些类型的(循环)引用可能会导致IE7内存泄漏
- 何时传递宿主对象以及何时传递本机对象(在函数中)
编辑3:
我从Anurag的例子中获得了一些见解。
*@Anurag:*如果我正确理解了您的示例,那么关键点是为事件处理程序设置正确的执行上下文(但正确的设置取决于您的需要)。在您的例子中,这是表示对象实例,它是通过Mootool的bind()函数完成的。所以你要确保你总是处理包装器对象(我称之为本机对象)而不是DOM对象,对吗?
给读者的一个提示:您不必被迫使用Mootools来实现这一点。在jQuery中,您可以使用*$.proxy()*函数设置事件处理程序,或者如果您使用的是纯旧Javascript,则可以使用每个函数公开的*apply*属性。您可以使用全局注册表:
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缓存对象以供以后使用。但您可以使用以下方法扩展上述建议的WidgetRegistry
:
通过这种方式,您可以存储附加到每个jQuery对象的小部件ID,并从全局注册表重新访问它
测试,如果jQuery对象具有现有小部件:
return $('#test').data('widget') &&
($('#test').data('widget') in window.WidgetRegistry);
请注意,这些只是建议。实际上,有几十种方法可以实现这样的整合。如果您想将代码与jQuery更深入地结合起来,您可以这样做,这样您就可以编写如下内容:
$('#widget').widget({'foo':'bar'});
// and/or
var allWidgets = $('*:widget');
// ...
我不确定我是否完全理解了你的问题,但我会尝试提出一些想法 在我看来,您应该创建一个小部件基类,它包含小部件的通用功能 让我们使用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);
}
对于需要同步的四个对象,可以使用单个对象并在构造函数中传递引用,或者作为函数参数传递引用 我解决这个问题的方法是永远不要丢失对包装器对象的引用。每当需要DOM对象时(例如插入页面),这个包装器对象就会提供它。但是将该小部件粘贴到屏幕上,包装器对象设置了所有特定于该小部件的事件处理和AJAX处理代码,因此在这些事件处理程序和AJAX回调中始终维护包装器的引用
我使用MooTools在jsfiddle上创建了一个对您来说可能有意义的工具。您能做什么或不能做什么,很大程度上取决于您对javascript的控制程度。就我个人而言,我经常不得不使用其他人构建的库,所以我可能只能使用DOM节点,但我确实需要我的对象。在这些情况下,我发现
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);
}
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);