【问题标题】:Real-time data in a grid - better method网格中的实时数据 - 更好的方法
【发布时间】:2012-05-14 17:31:57
【问题描述】:

在网格中显示实时数据(证券交易所、天气等)的更好方法是什么?
我用这个方法:

setInterval(function(){
      jQuery("#list1").trigger('reloadGrid');
}, 5000);

【问题讨论】:

    标签: jqgrid


    【解决方案1】:

    我觉得你的问题很有趣。我认为这个问题对于许多其他用户来说可能很有趣。所以我要 +1。

    setInterval 的使用使我在常见的独立于浏览器的情况下最好的方式。应该将setInterval 的结果保存在一个变量中,以便能够使用clearInterval 来停止它。

    另一个小的改进是使用[{current:true}](详见the answer)作为trigger的第二个参数:

    var $grid = jQuery("#list1"), timer;
    
    timer = setInterval(function () {
        $grid.trigger('reloadGrid', [{current: true}]);
    }, 5000);
    

    它会在重新加载网格时保存选择。

    许多新的网络浏览器现在支持WebSocket。因此,如果网络浏览器支持它,最好使用这种方式。这样一来,如果服务器上的数据未更改并防止服务器永久池化,则可以跳过不必要的网格重新加载。

    一种更常见的方式在我看来也很有趣。如果可以使用网格数据最后更改的某种时间戳,则可以验证数据是否已更改。为此,可以使用ETag 或服务器上的一些通用附加方法。

    填充jqGrid的jQuery.ajax的当前success回调允许您使用beforeProcessing回调来修改服务器响应,但它不允许您停止jqGrid刷新 基于jqXHR.satus 值(在当前jqGrid 代码中为xhr.status)。对 jqGrid 代码的the line 进行小修改将允许您在服务器上的数据未更改的情况下停止 jqGrid 刷新。可以为"notmodified" 测试textStatus(在当前jqGrid 代码中为st)或为304 测试jqXHR.satus(在当前jqGrid 代码中为xhr.status)。重要的是要使用该场景,您应该使用prmNames: { nd:null } 选项并设置ETagCache-Control: private, max-age=0(有关更多信息,请参阅hereherehere)。

    更新:我尝试根据我最后的建议创建演示项目,发现它不像我上面描述的那么容易。尽管如此,我还是让它工作了。困难在于无法从 jQuery.ajax 内部的服务器响应中看到 304 代码。原因是XMLHttpRequest specification中的以下地方

    对于用户代理导致的 304 Not Modified 响应 生成的条件请求用户代理必须像服务器一样操作 给出了带有适当内容的 200 OK 响应。用户代理 必须允许作者请求标头覆盖自动缓存 验证(例如 If-None-MatchIf-Modified-Since),在这种情况下 304 Not Modified响应必须通过。

    所以人们在$.ajaxsuccess 处理程序中看到200 OK 状态,而不是来自服务器响应的304 Not Modified。似乎XMLHttpRequest 从缓存中获取完整响应,包括所有 HTTP 标头。所以我决定改变对缓存数据的分析,只是将上次 HTTP 响应中的 ETag 保存为新的 jqGrid 参数,并用保存的数据测试新响应的 ETag

    我看到你使用 PHP(我不使用 :-()。不过我可以阅读和理解 PHP 代码。我希望你能以同样的方式阅读 C# 代码并理解主要思想。所以你会能够在 PHP 中实现相同的功能。

    现在我描述我做了什么。首先我修改了jqGrid源代码的the lines,它使用了来自

    beforeProcessing回调
    if ($.isFunction(ts.p.beforeProcessing)) {
        ts.p.beforeProcessing.call(ts, data, st, xhr);
    }
    

    if ($.isFunction(ts.p.beforeProcessing)) {
        if (ts.p.beforeProcessing.call(ts, data, st, xhr) === false) {
            endReq();
            return;
        }
    }
    

    它将允许从 beforeProcessing 返回 false 以跳过数据刷新 - 跳过数据处理。我在demo中使用的beforeProcessing的实现是基于ETag的用法:

    beforeProcessing: function (data, status, jqXHR) {
        var currentETag = jqXHR.getResponseHeader("ETag"), $this = $(this),
            eTagOfGridData = $this.jqGrid('getGridParam', "eTagOfGridData");
        if (currentETag === eTagOfGridData) {
            $("#isProcessed").text("Processing skipped!!!");
            return false;
        }
        $this.jqGrid('setGridParam', { eTagOfGridData: currentETag });
        $("#isProcessed").text("Processed");
    }
    

    $("#isProcessed").text("Processed"); 行或$("#isProcessed").text("Processing skipped!!!"); 行在 div 中设置了 "Processed""Processing skipped!!!" 文本,我用这些文本直观地表明来自服务器的数据用于填充网格。

    在演示中我显示了两个具有相同数据的网格。我用来编辑数据的第一个网格。第二个网格每秒从服务器中提取数据。如果服务器上的数据未更改,则 HTTP 流量如下所示

    HTTP 请求:

    GET http://localhost:34336/Home/DynamicGridData?search=false&rows=10&page=1&sidx=Id&sord=desc&filters= HTTP/1.1
    X-Requested-With: XMLHttpRequest
    Accept: application/json, text/javascript, */*; q=0.01
    Referer: http://localhost:34336/
    Accept-Language: de-DE
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; LEN2)
    Host: localhost:34336
    If-None-Match: D5k+rkf3T7SDQl8b4/Y1aQ==
    Connection: Keep-Alive
    

    HTTP 响应:

    HTTP/1.1 304 Not Modified
    Server: ASP.NET Development Server/10.0.0.0
    Date: Sun, 06 May 2012 19:44:36 GMT
    X-AspNet-Version: 4.0.30319
    X-AspNetMvc-Version: 2.0
    Cache-Control: private, max-age=0
    ETag: D5k+rkf3T7SDQl8b4/Y1aQ==
    Connection: Close
    

    因此,如果数据未更改,不会从服务器传输任何数据。如果数据发生变化,HTTP 标头将以HTTP/1.1 200 OK 开头,包含新修改数据的ETag 以及数据页本身。

    可以使用导航器的“刷新”按钮手动刷新网格数据,也可以使用每秒执行$grid1.trigger('reloadGrid', [{ current: true}]); 的“开始自动刷新”按钮刷新网格数据。页面看起来像

    我在页面底部用颜色框标记了最重要的部分。如果有一个loadui: "disable" 选项,那么在拉动服务器期间根本看不到网格上的任何变化。如果有人评论了一个选项,即会在很短的时间内看到“正在加载...” div,但 没有网格包含会闪烁

    启动“自动刷新”后,您会看到如下图所示

    如果要更改第一个网格中的某些行,第二个网格将在一秒钟内更改,并且将看到 "Processed" 文本,然后再更改为 "Processing skipped!!!" 文本。

    服务端对应的代码(我用的是ASP.NET MVC)大多如下

    public JsonResult DynamicGridData(string sidx, string sord, int page, int rows,
                                      bool search, string filters)
    {
        Response.Cache.SetCacheability (HttpCacheability.ServerAndPrivate);
        Response.Cache.SetMaxAge (new TimeSpan (0));
    
        var serializer = new JavaScriptSerializer();
        ... - do all the work and fill object var result with the data
    
        // calculate MD5 from the returned data and use it as ETag
        var str = serializer.Serialize (result);
        byte[] inputBytes = Encoding.ASCII.GetBytes(str);
        byte[] hash = MD5.Create().ComputeHash(inputBytes);
        string newETag = Convert.ToBase64String (hash);
        Response.Cache.SetETag (newETag);
        // compare ETag of the data which already has the client with ETag of response
        string incomingEtag = Request.Headers["If-None-Match"];
        if (String.Compare (incomingEtag, newETag, StringComparison.Ordinal) == 0) {
            // we don't need return the data which the client already have
            Response.SuppressContent = true;
            Response.StatusCode = (int)HttpStatusCode.NotModified;
            return null;
        }
    
        return Json (result, JsonRequestBehavior.AllowGet);
    }
    

    我希望不仅使用 ASP.NET MVC 的人也能清楚代码的主要思想。

    您可以下载项目here

    更新:我发布了the feature request 以允许beforeProcessing 通过返回false 值来中断服务器响应的处理。相应的更改已经包含在主 jqGrid 代码中(参见here)。所以 jqGrid 的下一个版本将包含它。

    【讨论】:

    • 不是每隔 x 秒重新加载一次网格,难道不能直接在行中“写入”吗?
    • @Bertaud:对不起,我不明白你的意思。无论如何,我想你总是使用 jqGrid 的gridview: true 选项。因此,通过将innerHTML 设置为表格主体,将使用一个操作更新整个网格主体。如果您要单独修改jqGrid的许多行甚至单元格,则重新加载整个网格可能会很慢。此外,如果添加或删除了某些行,则也必须修改寻呼机。所以重新加载网格似乎是我最好的方法。
    • @Bertaud:您应该了解页面上 one 元素的变化,然后重新计算页面上 all 其他现有元素的位置(回流)。因此减少页面的更改次数可能是提高页面性能最重要的。请参阅here,了解有关最小化浏览器重排的更多信息。
    • 我正在使用 current:true 但不在触发器中。我的意思是在行中写入 jQuery("#list1").updateGridRows(s,"id");与 s = 数组。那可以改善刷新吗?
    • @SuhailGupta: $grid.trigger('reloadGrid', [{current: true}]) 重新加载整个网格体。问题只是代码究竟做了什么,datatype 有网格、网格的页面大小以及您的确切场景的许多其他部分。答案很古老,jqGrid 现在有很多新功能。无论如何,如果您确定只更改了一行并且您获得了该行的数据,则可以使用setRowData 方法。另一方面,网格中一行的变化可以跟随页面上其他行和其他 HTML 元素的位置的变化。这样网络浏览器重排就完成了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-09
    • 1970-01-01
    • 1970-01-01
    • 2018-01-15
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多