如何在复杂的Web应用程序中构建Javascript程序?

pri*_*bel 27 javascript architecture web-applications

我有一个问题,不容易描述.我正在编写一个强大的jQuery和AJAX调用的Web应用程序.现在我在Javascript archicture中没有很多经验,但我意识到我的程序没有很好的结构.我认为我有太多的标识符指的是同一个(至少或多或少)的东西.

让我们看看构成应用程序的一小部分的任意示例性UI小部件:小部件可以是窗口的一部分,窗口可以是窗口管理器的一部分:

  1. 事件处理程序使用DOM元素作为参数.DOM元素表示浏览器中的小部件.
  2. 很多时候我使用jQuery对象(基本上是围绕DOM元素的包装器)来处理小部件.有时它们是短暂使用的,有时它们存储在变量中以供以后使用.
  3. AJAX函数调用这些小部件使用字符串标识符.它们是服务器端处理的.
  4. 除此之外,我有一个widget类,其实例代表一个小部件.它通过new运算符实例化.

现在我有不同的四个不同的对象标识符用于相同的事情,需要保持同步,直到页面重新加载.这似乎不是一件好事.

有什么建议?



编辑:
@Will Morgan:它是一个表单设计器,允许在浏览器中创建Web表单.后端是Zope,一个python Web应用程序服务器.很难更明确,因为这是我在使用trio jQuery,DOM树和我自己的原型类实例进行Javasscript开发时一直观察到的一般问题.

EDIT2:
我认为做一个例子虽然是一个人为的例子会有所帮助.下面您将看到一个记录器小部件,可用于将块元素添加到显示已记录项目的网页.

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?');
Run Code Online (Sandbox Code Playgroud)

此时,我有一个围绕HTMLDivElement(托管对象)的包装器.有了logger实例(本机对象),我可以通过函数logger.getRootEl()轻松地使用它.
我遇到困难的地方是我手头只有DOM元素,需要对函数makeLogger返回的公共API做一些事情(例如在事件处理程序中).而这就是混乱开始的地方.我需要将所有本机对象保存在存储库或其他东西中,以便我可以再次检索.从托管对象到我的本机对象的连接(例如对象属性)会更好.我知道可以做到,但它有一些缺点:

  • 这些(循环)引用可能会泄漏到IE7
  • 何时传递托管对象以及何时传递本机对象(在函数中)?

现在,我使用jQuery的data()方法进行后引用.但总而言之,我不喜欢跟踪托管对象与其原生对象之间关系的方式.

你如何处理这种情况?



EDIT3:
经过一些见解后,我从Anurag的例子中
获益 .. @Anurag:如果我理解你的例子是正确的,关键点是设置正确的(正确的取决于你的需要)事件的执行上下文处理程序.在您的情况下,这是使用Mootool的bind()函数完成的表示对象实例.因此,您确保始终处理包装器对象(我称之为本机对象)而不是DOM对象,对吧?

给读者的一个注释:你没有被迫使用Mootools来达到这个目的.在jQuery中,您可以使用$ .proxy()函数设置事件处理程序,或者如果您使用普通的旧Javascript,则可以使用每个函数公开的apply属性.

Bol*_*wyn 5

您可以使用全局注册表:

window.WidgetRegistry = {};
window.WidgetRegistry['foowidget'] = new Widget('#myID');
Run Code Online (Sandbox Code Playgroud)

当AJAX调用返回时,他们可以像这样获取小部件:

var widgetID = data.widgetID;
if (widgetID in window.WidgetRegistry) {
    var widget = window.WidgetRegistry[widgetID];
}
Run Code Online (Sandbox Code Playgroud)

对于你的jQuery调用:我猜他们相对便宜,因为jQuery缓存对象供以后使用.但你可以WidgetRegistry通过使用扩展以上建议.data():

var $widget = $('#myWidget');
var widgetID = 'foo';
$widget.data('widget', widgetID);
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以存储附加到每个jQuery对象的窗口小部件ID,并从全局注册表重新访问它.

测试,如果jQuery对象具有现有小部件:

return $('#test').data('widget') &&
       ($('#test').data('widget') in window.WidgetRegistry);
Run Code Online (Sandbox Code Playgroud)

请注意,这些只是建议.实际上,有很多方法可以实现这样的整合.如果你想将你的代码与jQuery更深层地结合起来,你可以扩展jQuery对象,这样你就可以编写如下内容:

$('#widget').widget({'foo':'bar'});
// and/or
var allWidgets = $('*:widget');
// ...
Run Code Online (Sandbox Code Playgroud)


Anu*_*rag 2

对于需要同步的四个对象,您可以拥有一个对象并在构造函数中传递引用,或作为函数参数传递引用。

我解决这个问题的方法是永远不要丢失对包装器对象的引用。每当需要 DOM 对象(例如插入到页面中)时,此包装器对象都会提供它。但是,将该小部件粘贴到屏幕上时,包装器对象会设置特定于该小部件的所有事件处理和 AJAX 处理代码,因此在这些事件处理程序和 AJAX 回调中始终维护包装器的引用。

我使用 MooTools 在 jsfiddle 上创建了一个可能对您有意义的简单示例。