簡體   English   中英

如何在復雜的 Web 應用程序中構建 Javascript 程序?

[英]How to structure Javascript programs in complex web applications?

我有一個問題,不容易描述。 我正在編寫一個 Web 應用程序,它充分利用了 jQuery 和 AJAX 調用。 現在我在 Javascript 架構方面沒有很多經驗,但我意識到我的程序沒有一個好的結構。 我想我有太多的標識符指代相同的(至少或多或少)事物。

讓我們看一下構成應用程序一小部分的任意示例 UI 小部件:小部件可能是窗口的一部分,窗口可能是窗口管理器的一部分:

  1. 事件處理程序使用 DOM 元素作為參數。 DOM 元素代表瀏覽器中的小部件。
  2. 很多時候我使用 jQuery 對象(基本上是圍繞 DOM 元素的包裝器)來處理小部件。 有時它們被暫時使用,有時它們被存儲在一個變量中供以后使用。
  3. AJAX 函數調用使用這些小部件的字符串標識符。 它們在服務器端處理。
  4. 除此之外,我有一個小部件類,它的實例代表一個小部件。 它是通過 new 運算符實例化的。

現在我有四個不同的對象標識符用於同一件事,需要保持同步,直到重新加載頁面。 這似乎不是一件好事。

有什么建議嗎?



編輯:
*@Will Morgan*:它是一個表單設計器,允許在瀏覽器中創建 Web 表單。 后端是 Zope,一個 Python Web 應用程序服務器。 很難說得更清楚,因為這是我在使用 jQuery、DOM 樹和我自己的原型類實例進行 Javascript 開發時一直觀察到的一個普遍問題。

編輯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 執行某些操作時(例如在事件處理程序中),我會卡住。 這就是混亂開始的地方。 我需要將所有本機對象保存在存儲庫或其他東西中,以便我可以再次檢索。 擁有從托管對象返回到我的本機對象的連接(例如對象屬性)會更好。 我知道它可以做到,但它有一些缺點:

  • 這些(循環)引用可能會導致 IE7 內存泄漏
  • and when to pass the (in functions)?何時傳遞以及何時傳遞本(在函數中)?

現在,我使用 jQuery 的 data() 方法進行反向引用。 但總而言之,我不喜歡我必須跟蹤托管對象與其本機對應對象之間關系的方式。

你如何處理這種情況?



編輯3:
在我從 Anurag 的例子中獲得了一些見解之后..
for the event handlers. *@Anurag:* 如果我理解你的例子是正確的,關鍵點是為事件處理程序設置正確的(正確的取決於你的需要) 在您的情況下,這是使用 Mootool 的 bind() 函數完成的演示對象實例。 所以你確保你*總是*處理包裝器對象(我稱之為本機對象)而不是 DOM 對象,對嗎?

You're not forced to use Mootools to achieve this. 您不必使用 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 緩存對象供以后使用。 但是您可以使用.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM