历史

Ajax 应用有时无法满足用户的期望,因为它们与浏览器的交互方式与静态网页不同。当 Ajax 应用无法与浏览器历史记录集成时,这一点往往很明显,并且会让用户感到沮丧。例如,用户希望浏览器能够使用后退和前进操作导航回以前访问过的页面。由于 Ajax 应用通常是一个运行 JavaScript 逻辑的单页面,而不是一系列页面,因此浏览器历史记录需要应用的帮助才能支持这种用例。值得庆幸的是,GWT 的历史机制使得历史记录支持变得相当简单。

  1. GWT 历史机制
  2. 历史标记
  3. 示例
  4. 超链接小部件
  5. 有状态应用
  6. 处理 onValueChange() 回调

GWT 历史机制

GWT 的历史机制与其他 Ajax 历史记录实现(如 RSH(真正简单的历史记录))有很多共同之处。基本前提是在 URL 片段标识符中跟踪应用的“内部状态”。这是可行的,因为更新片段通常不会导致页面重新加载。

这种方法有几个优点

  • 这是可靠地控制浏览器历史记录的唯一方法。
  • 它为用户提供了良好的反馈。
  • 它是“可书签的”。即,用户可以创建当前状态的书签并保存它,将其发送到电子邮件,等等。

历史标记

GWT 包含一个机制,可以帮助 Ajax 开发人员激活浏览器历史记录。对于每个要在历史记录中可导航的页面,应用都应生成一个唯一的历史标记。标记只是一个字符串,应用可以解析它来返回到特定状态。此标记将在浏览器历史记录中保存为 URL 片段(在地址栏中,# 后面),当用户在历史记录中后退或前进,或者点击链接时,此片段将传回应用。

例如,名为“page1”的历史标记将以如下方式添加到 URL 中

http://www.example.com/com.example.gwt.HistoryExample/HistoryExample.html#page1

当应用想要将占位符推送到浏览器的历史记录堆栈时,它只需调用 History.newItem(token)。当用户使用后退按钮时,将调用任何使用 History.addValueChangeHandler() 添加为处理程序的对象。应用需要根据新标记的值来恢复状态。

示例

要使用 GWT 历史记录支持,您必须先将 iframe 嵌入到 主机 HTML 页面 中。

<iframe src="javascript:''"
          id="__gwt_historyFrame"
          style="position:absolute;width:0;height:0;border:0"></iframe>

然后,在您的 GWT 应用中,执行以下步骤

  • 当您想要启用历史记录事件时,将历史标记添加到历史记录堆栈。
  • 创建一个实现 ValueChangeHandler 接口的对象,解析新标记(可通过调用 ValueChangeEvent.getValue() 获取),并将应用状态更改为匹配。

以下简短示例显示了如何每次用户在 TabPanel 中选择新标签时添加历史记录事件。

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TabPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class BrowserHistoryExample implements EntryPoint {

  TabPanel tabPanel;
  /**
   * This is the entry point method.
   */
  public void onModuleLoad() {
    tabPanel = new TabPanel();

    tabPanel.add(new HTML("<h1>Page 0 Content: Llamas</h1>"), " Page 0 ");
    tabPanel.add(new HTML("<h1>Page 1 Content: Alpacas</h1>"), " Page 1 ");
    tabPanel.add(new HTML("<h1>Page 2 Content: Camels</h1>"), " Page 2 ");

    tabPanel.addSelectionHandler(new SelectionHandler<Integer>(){
      public void onSelection(SelectionEvent<Integer> event) {
        History.newItem("page" + event.getSelectedItem());
     );

    History.addValueChangeHandler(new ValueChangeHandler<String>() {
      public void onValueChange(ValueChangeEvent<String> event) {
        String historyToken = event.getValue();

        // Parse the history token
        try {
          if (historyToken.substring(0, 4).equals("page")) {
            String tabIndexToken = historyToken.substring(4, 5);
            int tabIndex = Integer.parseInt(tabIndexToken);
            // Select the specified tab panel
            tabPanel.selectTab(tabIndex);
          } else {
            tabPanel.selectTab(0);
          }

        } catch (IndexOutOfBoundsException e) {
          tabPanel.selectTab(0);
        }
      }
    });

    tabPanel.selectTab(0);
    RootPanel.get().add(tabPanel);
  }
}

超链接小部件

超链接便于将历史记录支持纳入应用。超链接小部件是 GWT 小部件,看起来像普通的 HTML 锚点。您可以将历史标记与 Hyperlink 关联,当点击它时,历史标记将自动添加到浏览器的历史记录堆栈中。History.newItem(token) 步骤将自动完成。

有状态应用

处理有状态应用的历史记录需要特别注意。必须将足够的信息编码到历史标记中,才能将应用状态恢复到设置历史标记时的状态。应用还必须注意清除与导航回以前访问的页面无关的任何状态。

例如,一个呈现多页面问卷的应用可以将页面编号以及其他一些状态编码为标记。当呈现问卷中的新页面时,一个历史标记将被添加到历史记录堆栈中。请注意,对于有状态的应用(例如问卷),需要仔细考虑历史记录回调的实现方式。当使用标记返回页面时,需要一些逻辑来恢复先前状态。

标记 操作
info 导航到用户输入个人信息所在的页面。恢复以前输入的数据
page1 导航到问卷的第 1 页。恢复先前的答案。
page2 导航到问卷的第 2 页。恢复先前的答案。
pageN 导航到第 N 页...
end 导航到问卷的结尾。验证所有问题是否都已回答。确保不要重新提交问卷。

在上述情况下,可以导航回页面,但历史标记中没有足够的信息来恢复用户的先前答案。更好的标记编码将是如下语法

page=<pagename>;session=<sessionname>

其中 <pagename> 告诉应用导航到哪个页面,<sessionname> 是一个键,用于在数据库中查找用户先前输入的数据。

处理 onValueChange() 回调

处理 ValueChangeHandler 中的 onValueChange() 回调方法的第一步是使用 ValueChangeEvent.getValue() 获取新的历史标记;然后,您需要解析该标记。请记住,您的解析需要健壮!用户可能会手动输入 URL,或者可能保存了来自应用旧版本的 URL。解析完标记后,您就可以重置应用的状态了。

当调用 onValueChange() 方法时,您的应用必须处理两种情况

  1. 应用刚刚启动,并传递了一个历史标记。
  2. 应用已经在运行,并传递了一个历史标记。

在第一种情况下,应用必须在处理状态标记之前正确初始化自身。在第二种情况下,应用的某些部分可能需要重新初始化。