【问题标题】:Backbone and jqm: back button how to restore page contextBackbone 和 jqm:后退按钮如何恢复页面上下文
【发布时间】:2013-10-07 05:40:26
【问题描述】:

我正在使用 Backbone、require 和 jQuery mobile 作为技术堆栈构建一个 HTML5 移动应用程序。该应用程序与后端 Web 服务等的连接非常流畅。 要在页面之间进行更改,我使用 jQuery changePage。 为了实例化 Backbone 视图,我使用以下策略:

$( document ).delegate("#card-delivery-address", "pageshow", function(){
    require(["js/views/cardDeliveryAddressViews.js" ], function(cardDeliveryAddressViews) {
        new cardDeliveryAddressViews();
    });
});
  1. $.mobile.changePage('deliveryAddress.html') => 改变当前 使用 jquery mobile 的页面

  2. 当在 #card-delivery-address 上触发名为“pageshow”的事件时 (这意味着我的页面已插入 DOM 并成功 render) => 创建主干视图并将 $el 绑定到 现有的 DOM 并使用主干控制 DOM 事件 意见。

  3. 要在视图之间传递数据和实例,我们使用 window.tempData 我们在其中放置上下文数据的全局变量,以便新的 view 会知道该怎么做。

单向导航是成功的,假设我来自 view1 --> view2 (with tempData) 然后来自 view2 --> view 3 (覆盖相同的 tempData)。现在,这是我的问题:如果我们想从视图 3-> 视图 2 返回,我们将需要用于初始化和渲染视图 2 的 tmpData 内容。当我们想返回视图 1 时也是如此.

注意:我没有使用骨干路由器,但如果这能解决我的问题,我可以改用它。

各位有什么想法吗?

【问题讨论】:

  • 我曾经覆盖 Backbone.history.navigate,所以它在导航时使用 HTML5 replaceState 推送状态对象。我会通过要求视图提供他们想要保留的任何信息来组装这个对象,分层。在路由事件中,我会从历史 API 中获取此状态对象并将其“返回”给视图。花了几天的时间,但效果非常好。
  • 我做了类似的事情,但与 Backbone 无关。它在 jquery 之上工作并使用 HTML 5 历史事件。它也很好用

标签: jquery jquery-mobile backbone.js backbone-views backbone-routing


【解决方案1】:

好的,让我们再试一次。我们将实现一个以页面路径为键的数据存储。

var pagesData = {};

// Let's assume we just switched to page '/my/page/1'

// Get a reference to the stored page data if it exists, 
// or create it empty and return it.
var currentPageData = pagesData[window.location.pathname] ||  
                     (pagesData[window.location.pathname] = {});

currentPageData.foo = 'bar';

console.log(pagesData['/my/page/1'].foo); // > "bar"

现在您的所有页面都有一个“本地”数据存储,允许它们在整个导航过程中保存状态/数据。

注意:如果不使用 pushState,则必须使用 window.location.hash 代替 window.location.pathname 作为 PagesData 中的键。

【讨论】:

  • 一个视图可以在同一个堆栈中出现 2 次,但具有不同的上下文。
  • 所以问题是我可以用什么来区分视图实例,而不是使用 window.location.pathname ?
  • 你可能在架构层面有问题。让视图数据持久保存的最佳方法可能是一开始就不要销毁并重新创建它们。
  • 你仍然可以使用路径名作为键,在页面内添加一些独特的东西作为后缀。 pagesData[window.location.pathname + '#viewNameUniqueInThisPage']。当然,您正在缓存的数据应该与 仅视图状态 相关,而不是以任何方式与可以以许多其他方式缓存和重用的数据建模(谷歌骨干模型缓存)。
  • @HoucemBerraana 看起来您的代码确实有点味道。尽量保持视图状态和模型数据不同。
【解决方案2】:

在页面转换期间,前一页将从 DOM 中删除

要将所有以前访问过的页面保留在 DOM 中,您可以添加属性:

<div data-role="page" id="card-delivery-address" data-dom-cache="true">              
    <!-- [...] -->      
</div>

或者通过初始化全局参数:

$.mobile.page.prototype.options.domCache = true;

【讨论】:

  • 当我可以在历史堆栈中显示一个页面的实例时,这可以解决部分问题。但是,如果在同一个历史堆栈中,我们得到同一个页面的 2 个实例,我们就会卡住
【解决方案3】:

为什么不让windows.tempData 成为一个对象?

windows.tempData = {};

存储页面数据:

window.tempData["id_of_page"] = your_data;

开启pagebeforeshow

myDataForThisPage = window.tempData[this.id];  // assuming "this" being your page element

【讨论】:

  • 我不能,因为一个 ID 可以在 DOM 中多次放置。当页面不再在内存中时,我也想进行一些清理
  • 多个 ID 不是有效的 HTML,必然会导致问题。另外,我认为您的问题是关于当用户重新访问页面时从不再位于 DOM 中的页面访问 tempData ?如果是这样,那么您需要存储并保留此 tempData。如果你删除它,当用户返回时你当然不会拥有它。
  • 好吧,您可以尝试为 JQM urlhistory 编写一个扩展(虽然不确定这是否可以像其他所有内容一样扩展)。我曾经为多面板布局“破解”JQM 导航,为此,我还将视口/页面容器与 JQM 保留的数据一起存储在 urlHistory 中。所以(a)自己破解你的临时数据或(b)看看你是否可以在 url 历史记录上创建一个扩展。无论哪种方式都是我首选的解决方案,因为您没有添加任何额外的逻辑层。如果您愿意,我可以挖掘出如何修改 url 历史记录的示例
  • 不,不……我不是这个意思。检查JQM JSBin。打开 firebug 和控制台 $.mobile.navigate.history。这是 JQM 保留它自己的“历史”并存储某些信息的地方。参数 stack 包含用户访问过的所有页面以及 JQM 当前保存在其历史记录中的所有页面。 Stack[0] 是存储 hashlastScrollurl 的当前页面。这是您应该为用户访问的每个页面添加 tempData 作为第 4 个参数的地方。如果 JQM 从堆栈中删除,您的 tempData 也会自动清理
  • 非常感谢。我会尝试看看它是如何工作的。这就是我要找的
【解决方案4】:

thibauts' answer 似乎是正确的方向。一定要读一读。

我理解你的问题的方式。任何时候打开一个“page3a”取决于前一页。如果它是“page2a”,它将以某种方式工作。如果它是“page2b”,它将以不同的方式工作。这些信息保存在每个页面的 tempData 中。

骨干路由无法解决这个问题。因为它们只是提供将 URL 路由转换为执行序列的方法。您仍然需要管理 tempData。 最终的解决方案取决于 tempData 的大小以及您如何管理它。是否有一些共享数据可以从 tempData 结构中取出并保存在共享结构中。是否有一些数据可以自己保存,很少更改并且可以实现某种memento pattern

有三种情况-

  1. tempData 相对较小,您可以保留一些副本 - 在这种情况下,您可以在circular buffer 中保留 tempData 的固定历史记录 [比如最后 10 页]。继续删除最旧的项目并在每次页面访问时添加新项目。如果用户没有更改任何内容并专门尝试前进按钮/导航,这将为您提供thibauts 所说的后退导航和历史跳转以及后退导航后的前进导航。

    1.1 EDIT :如果您希望保留已经支持的转发操作,您仍然可以将其保留在循环队列中, 但是这样你必须区分view2的两次访问 (见 cmets)。并将一切视为向前迈进 历史(循环队列)。另一种方法是保留一个指针(数组 指数。如果可以,请参考)缓冲区中的当前页面和 当添加新页面(在某些操作上)到缓冲区时移动指针 那个。进行向后/向前移动时,只需将点移入 缓冲和渲染。 [如果实施起来太复杂或成本太高,您 一旦用户从视图/页面移回,就可以完全忽略它。 只需将其从历史记录中删除,用户就不能再次使用 fwd。或返回 在 back-new-back-back 之后两次到达它。骨干网.router有办法 历史替换]

  2. tempData 足够大,您只能保留它的几个副本 [假设少于 5 个]。在这种情况下,为上一页保留一份副本。并保留历史中特定关键页面的副本,其中用户返回的可能性很高。例如,在购物流程中,用户可能会比地址页面更频繁地返回购物车。为此,您将需要一个特殊的数据结构,您可以在其中维护每个页面的优先级。优先级基于时间和一些偏差标准。最新的获得高时间参数,最重要的获得高偏置参数。每当您访问新页面时。删除优先级最低的 tempData。

  3. tempData 太大了 - 放弃返回导航。在历史记录中保留几个点,这些点类似于重置点(如果可能),并且您可以从头开始重新创建 tempData。

如果除了 tempData 之外还有其他瓶颈。请发表评论。

【讨论】:

  • 感谢您的评论。实际上是的,我同意 thibatus 是正确的,但更复杂。如果您跟随浏览器的后退和上一个按钮,您可以看到堆栈。以及浏览器实现的方式:view 3 -> view 2(view 3 context 没有被删除并且仍然在堆栈中)然后如果 view 3 -> back view2 -> view 4 :它删除了 view 3 的上下文。这就是优化我在找。没有找到相应的 js 库
  • 你的意思是首先你到达view2然后移动到view3a。 (view2 -> view3a)。现在回来了。 (view3a->view2) 和一个不同的 fwd (view2 -> view3b) 这样你就失去了 view3a?
  • 是的,我认为应该这样处理内存。这样我可以确保我的上下文与历史堆栈同步
  • 我添加了一个编辑。看一看。正如@thibauts 所说的架构问题。您应该先重新考虑结构,然后再考虑解决方法。
  • 是的,这是有道理的,这就是为什么我在计划开发第二阶段和第三阶段的代码之前想问这个问题
【解决方案5】:

使用堆栈可能会解决您的问题。浏览器的内部历史处理使用它来处理导航和后退按钮。

堆栈是在 JavaScript 中使用 Array 及其 push()pop() 方法实现的。它们允许您保存和恢复以前的状态。

var navigationStack = {...},
    currentState = {...};

...
// When going to a new page, you push (or save) the current state on the stack
navigationStack.push(currentState);
...

...
// When going back to the previous page you pop the previous state from the stack
currentState = navigationStack.pop();
...

当然,如果您从 view3 导航到 view2 类似于“返回”操作,则此方法有效。如果是故意导航到之前可能已初始化的任何页面,您必须将页面状态保存在哈希中,按页面名称(或完整路由)索引。

您的机箱应该适合这两种型号中的一种。如果没有,要么你的要求没有很好地定义,要么我误解了你的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-17
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多