基于DOM的数据存储,最佳选择/表现?

时间:2013-07-22 12:38:18

标签: ajax dom data-binding data-structures load

这个问题与load data embedded into DOM structure有关。 我使用的是jQuery2,但这个问题对任何其他框架或单个Javascript代码都有效。

有两种情况:

  1. 当数据加载一次(带页面)时,不需要“刷新数据”。

  2. 某些事件导致数据刷新时。

  3. 可以使用其中一个或其他

    更改平均性能

    假设情景-2的典型情况,其中必须重新加载页面片段,使用新的HTML和新数据。因此,$('#myDiv').load('newHtmlFragment')将以任何方式使用...而且,对于jQuery程序员,使用AJAX,有两种方法可以加载“基于DOM的数据”:

    • 按HTML :将所有数据表达到“newHtmlFragment”HTML中。假设有许多段落,每个段落都像<p id="someId" data-X="someContent">...more content</p>
      每个data-X1="contentX1" data-X2="contentX2" ...都有一些“冗长的开销”,如果它不是面向XHTML的,那对于webservice脚本来说并不优雅(我使用的是PHP,我的数据是一个数组,而且我更喜欢使用json_encode)。
    • 通过jQuery评估:仅对$('#myDiv').load('newHtmlFragment')使用相同的<p id="someId">...more content</p>,而不使用data-X。第二个AJAX加载像$('#someId').data(...)这样的jQuery脚本并对其进行评估。所以这是一个开销,用于节点选择和数据包含,但是对于大项目数据,每个数据都可以被JSON包含。
    • 纯JSON :类似于“by jQuery”,但第二个AJAX加载了一个JSON对象,如var ALLDATA={'someId1':{...}, 'someId2':{...}, ...}。所以这是执行类似$('#myDiv p').each(function(){... foreach ... $(this).data('x',ALLDATA[id]['x']);})反向选择的静态函数的开销,但是对于大数据,所有数据都可以被JSON包含。

    问题:什么是最佳选择?这取决于场景或其他上下文参数?有显着的性能权衡吗?

    PS:完整的答案需要解决性能问题......如果没有显着的性能差异,最佳选择依赖于“最佳编程风格”和软件工程考虑。


    更多背景信息,如果您需要作为参考答案。我的实际问题是在场景-1中,我正在使用第二个选择,“通过jQuery脚本”,执行:

     $('#someId1').data({'bbox':[201733.2,7559711.5,202469.4,7560794.9],'2011-07':[3,6],'2011-08':[2,3],'2011-10':[4,4],'2012-01':[1,2],'2012-02':[12,12],'2012-03':[3,6],'2012-04':[6,12],'2012-05':[3,4],'2012-06':[2,4],'2012-07':[3,5],'2012-08':[10,11],'2012-09':[7,10],'2012-10':[1,2],'2012-12':[2,2],'2013-01':[6,10],'2013-02':[19,26],'2013-03':[2,4],'2013-04':[5,8],'2013-05':[4,5],'2013-06':[4,4]});
    
     $('#someId2').data({'bbox':[197131.7,7564525.9,198932.0,7565798.1],'2011-07':[39,51],'2011-08':[2,3],'2011-09':[4,5],'2011-10':[13,14],'2011-11':[40,42],'2011-12':[21,25],'2012-01':[10,11],'2012-02':[26,31],'2012-03':[27,35],'2012-04':[8,10],'2012-05':[24,36],'2012-06':[4,7],'2012-07':[25,30],'2012-08':[9,11],'2012-09':[42,52],'2012-10':[4,7],'2012-11':[17,22],'2012-12':[7,8],'2013-01':[21,25],'2013-02':[5,8],'2013-03':[8,11],'2013-04':[28,40],'2013-05':[55,63],'2013-06':[1,1]});
    
    $('#...').data(...);   ... more 50 similar lines...  
    

2 个答案:

答案 0 :(得分:3)

这个问题可以从不同方面进行讨论。我现在能想到的两个问题是软件工程和最终用户体验。首先覆盖的综合解决方案也可以涵盖后者,但通常不可能提出这样的解决方案(由于其成本)这两者几乎不重叠。

软件工程观点

在这个POV中,强烈建议系统的不同部分尽可能隔离。这意味着你最好尽可能晚地推迟数据和视图的结合。它可以帮助您将开发人员划分为两个独立的组;那些了解服务器端编程并且不知道HTML(或任何其他接口层技术)如何工作的人以及那些仅具有HTML和Javascript经验的人。仅仅这一部门就是管理层的福祉,它对于团队合作至关重要的大型项目非常有帮助。它还有助于维护和扩展系统,这是软件工程的目标。

用户体验观点

与先前的解决方案一样好,它带来(可解决的)缺点。正如您在问题中提到的,如果我们要单独加载视图和数据,它会提升我们必须发送到服务器以检索它们的请求数。它带来了两个问题,首先是每个请求带来的开销,第二个是用户必须等待每个请求被响应的延迟。第二个更明显,所以让我们从那开始吧。随着互联网和带宽的所有进步,但我们用户的超出预期迫使我们考虑这种延迟。减少请求数量的一种方法是您的首选:HTML片段中的数据。多个请求也存在开销问题。这可以通过HTTP协议的握手(在客户端和服务器端)以及每个请求将导致在服务器上加载会话这一事实来解释,这在大规模上可能非常可观。所以,你的第一个选择可能是这个问题的答案。

决胜局

有故事的双方说,那么呢?最终的解决方案是数据和视图在客户端结合但同时下载的组合。说实话,我不知道这样的图书馆。但原理很简单,您需要一种机制来在同一响应中打包数据和清空HTML片段,并将它们组合到用户将在客户端上看到的内容中。这个解决方案很实用(实施),但它有点费用,一旦付清,你可以终生受益。

答案 1 :(得分:2)

这取决于 您将如何使用存储的数据。考虑这个例子:

实施例

数据库中有300个项目(比如过去300天的服务器访问日志)。现在,您要显示300个<div>标记,每个标记代表一个数据库项。 现在有两种选择:

<div data-stat1="stat1" data-stat2="stat2" data-stat3="stat3">(...)</div>
<!-- again, this is repeated 300 times -->

<script>
// Example on how to show "stat1" value in all <div>s
function showStat1() {
  for(var i=1; i<=300; i+= 1) {
    var theID = '#id-' + i;
    jQuery(theID).text(jQuery(theID).data('stat1'));
  }
}
</script>

OR

<div id="id-1">(...)</div>
<!-- repeat this 300 times, for all DB items -->

<script>
data = { // JSON data which is displayed in the <div> tags
  '1': ['stat1', 'stat2', 'stat3'],
  // same for all 300 items
}

// Example on how to show "stat1" value in all <div>s
function showStat1() {
  for(var i=1; i<=300; i+= 1) {
    var theID = '#id-' + i;
    jQuery(theID).text(data[i][0]);
  }
}
</script>

哪种情况更好?

案例1 : 数据直接编码到DOM元素中,这使得这个数据易于实现。您可以在我的示例中看到第一个代码块生成的代码要少得多,代码也不那么复杂。

当您想要存储与DOM元素直接相关的数据时(因为您不必在JSON和DOM之间创建逻辑连接),此解决方案非常强大:访问正确的数据非常简单元件。

然而,你主要担心的是表现 - 这不是可行的方法。因为每次访问数据时,都必须首先通过javascript选择正确的DOM元素和属性,这需要相当长的时间。因此,当您想要读取/写入元素中的数据时,简单性会花费很多性能开销。

案例2 : 这种情况非常干净地将DISPLAY与DATA存储区分开。与第一种情况相比,它具有很大的优势。

A)数据不应与显示元素混合 - 想象你想从<div>切换到<table>突然你必须重写所有的javascript才能正确选择正确的表格单元格

B)无需遍历DOM树即可直接访问数据。想象一下,您想要计算所有值的平均总和。在第一种情况下,您需要循环所有DOM元素并从中读取值。在第二个中,您只需循环一个普通数组

C)可以通过ajax加载或刷新数据。您只传输JSON对象,但不传输显示数据所需的所有HTML内容

D)表现更好,主要是因为上述要点。 Javascript在处理简单数组或(不是太复杂)JSON对象方面要比从DOM树中过滤数据要好得多。

一般来说,这个解决方案的速度是第一种情况的两倍多。您还会发现 - 即使不是很明显 - 第二种情况也更容易编码和维护!代码更容易阅读和理解,因为您清楚地将数据与UI元素分开......

性能比较

您可以在此JSPerf方案中比较两个解决方案: http://jsperf.com/performance-on-data-storage

进一步研究

为了实现第二种情况,我通常使用这种方法:

  1. 我生成将用作UI的HTML代码。我经常需要通过javascript生成HTML,但现在我假设在加载页面后DOM树不会改变

  2. 在HTML的末尾,我包含一个带有JSON对象的标签,用于初始页面显示

  3. 通过jQuery onReady事件我然后解析JSON对象并根据需要更新UI元素(例如用数据填充表格)

  4. 当动态加载数据时,我只需使用新数据传输新的JSON对象,并使用与步骤3中完全相同的javascript函数来显示新结果。

  5. HTML文件示例:

    <div>ID: <span class="the-id">-</span></div>
    <div>Date: <span class="the-date">-</span></div>
    <div>Total page hits: <span class="the-value">-</span></div>
    <button type="button" class="refresh">Load new data</button>
    
    <script src="my-function.js"></script>
    <script>
    function initial_data() {
        return {"id":"1", "date":"2013-07-30", "hits":"1583"};
    }  
    </script>
    

    “my-function.js”文件:

    jQuery(function initialize_ui() {
        // Initialize the global variables we will use in the app
        window.my_data = initial_data();
        window.app = {};
        app.the_id = jQuery('.the-id');
        app.the_date = jQuery('.the-date');
        app.the_value = jQuery('.the-value');
        app.btn_refresh = jQuery('.refresh');
    
        // Add event handler: When user clicks refresh button then load new data        
        app.btn_refresh.click(refresh_data);
    
        // Display the initial data
        render_data();
    });
    
    function render_data() {
        app.the_id.text(my_data.id);
        app.the_date.text(my_data.date);
        app.the_value.text(my_data.hits);
    }
    
    function refresh_data() {
        // For example fetch and display the values for date 2013-07-29
        jQuery.post(url, {"date":"2013-07-29"}, function(text) {
            my_data = jQuery.parseJSON(text);
            render_data();
        });
    }
    

    我没有测试这段代码。此外还有重要的错误处理和其他优化缺失。但它有助于说明我试图描述的概念