简介:基于 Ajax 的 Web 应用最为明显的特征在于使用了浏览器内部原生支持的 XMLHttpRequest 对 象与后台服务器进行数据通信,由于这种通信方式不需要页面的刷新动作,因而无论与后台发生了多少次 通信,浏览器的 URL 会一直保持在初始地址不变。这随之而来的一个问题便是不断变化的页面状态信息 无法记录到浏览器的历史记录堆栈中,从而使得用户无法通过浏览器的前进 / 后退按钮在不同状态页面 间进行切换。Dojo 作为当今最为流行的 JavaScript 开发工具包之一,在其最新发布版本 1.4 中,专门 提供了 dojo.back 和 dojo.hash 的两种方法用于解决此类问题。本文旨在分析比较两种方法背后所采用 技术原理的基础之上,给出基于两种方法构建的示例解决方案,并探讨如何针对不同类型的 Ajax 应用选 择一套合适自身的解决方案。
问题解决的基本思路和技术基础
浏览器能够支持在用户访问过的页面间进行前进 / 后退的操作,依赖于内部维持的 history 对象。 出于安全性的考虑,浏览器并不允许 JavaScript 脚本对该对象进行增删改之类写操作,而只是可以通过 history. back/forward() 等方法进行访问。既然在页面状态发生变化时,无法通过脚本直接去影响浏览 器的历史信息,那么只有通过 URL 的变化来触发浏览器增加一条新的历史记录。这也就是说需要将 Ajax 应用的不同页面状态与 URL 进行一种一对一的映射,并且能够在回退或前进到某一 URL 之时,应用本身 能够在页面无刷新的情况下跳转到正确的页面状态。
那么,如何对 Ajax 应用的初始 URL 进行改变,而同时这种变化的切换又不会引起页面的重新加载呢 ?答案只有一个,那就是借助用于页面内资源片段定位目的的“片段标识符”(fragment identifier) ,即 URL 中“#”符号后的字符串(hash string)。当浏览器向服务器端请求资源时,片段标识符并不 会连同 base URL 一同发往服务器端,而只是在得到服务器返回的结果之后帮助浏览器快速定位到被相应 的锚点(anchor)所标识的资源片段,即使无法找个对应的锚点,浏览器也并不会报错。正是基于浏览器 的这一特性,构建片段标识符与页面状态之间的映射关系成为了解决此类问题的基础。
目前,除 IE 之外的浏览器都能够将因 hash 字符串改变而产生的新 URL 作为一条新的记录加入到浏 览器的历史堆栈中,通过对 window.location.hash 属性的读写,JavaScript 脚本可以获取并设置 hash 字符串,从而间接达到通过脚本了影响浏览器历史记录的目的。这里需要特别指出的是,IE 并不认为有 必要将非法片段标识符引发的变化等同于 URL 发生了变化,也便不会进行任何记录历史信息的操作,这 时最为常见的一种解决方法是在页面中嵌入一个隐藏 iframe,由于浏览器可以对 DOM 树中 iframe 节点 的 src 属性进行历史记录跟踪,这样通过在逻辑上建立一条“页面 URL -- 页面内 iframe URL -- 页面 状态”的对应链,同样可以在 IE 中建立片段标识符与页面状态的联系。
dojo.back 与 dojo.hash 实现原理的比较
既然已经明确了问题的解决方式,那么转到具体实现的角度上来,建立这种页面 URL 与页面状态之间 的映射关系,存在着两种实现方式:其一是以页面状态的迁移为主导,在状态首先改变之后,为页面 URL 添加相应的 hash 字符串,这种方式为 dojo.back 所采用;其二则是以 URL 的变化为主导,在状态变化 之前改变页面的 hash 字符串,而后页面再根据字符串中所包含的信息完成页面状态的迁移任务,这种方 式则是被 dojo.hash 所建议。接下来的部分,本文将对这两种方式背后的实现机理进行一个更为深入透 彻的分析介绍。
dojo.back 实现原理
Dojo 自其 1.0 发行版本起就开始向开发人员提供基于 dojo.back 的关于浏览器历史记录问题的解决 方案。Dojo.back 支持两种表现形式的前进 / 后退操作,二者不同之处体现在当页面状态进行改变的同 时,URL 是否也随之一同发生变化。更具体地来说,就是开发人员可以选择在不改变浏览器 URL 的情况 下,为 Ajax 应用增添前进后退的能力。
之所以这样设计,是因为 dojo.back 认为在不需要为 Ajax 应用增添“书签收藏”这种类似能力时, 没有必要将页面的变化体现到 URL 上面,用户始终面对一个固定不变的 URL 就可以了。而这种灵活的设 计方式,也完全是 dojo.back 内部特定的实现机理所决定的。