一体化的位置/哈希变更历史记录管理库

42
首先,我知道有提供location.pushState/popState的polyfills库(如History.jsHash.jsjQuery hashchange),因此请不要只提供这些链接。
我需要一个更强大的库来实现在RIA中执行以下操作:
  1. 用户点击链接
  2. 通知库并通过Ajax加载上下文(无完全重新加载!)
  3. 所有<a>元素都会使用单击处理程序进行利用,该处理程序
    • 防止在2.中重新加载页面(preventDefault)和
    • 调用location.pushState / 为旧浏览器设置location.hash
  4. 插入已加载的内容并替换当前内容
  5. 继续进行第1步。
此外,之前加载的内容应在用户后退时恢复。
例如,在Internet Explorer&lt;10和任何其他浏览器中通过Google+进行单击。
有什么东西甚至接近吗?我需要IE8、FF10、Safari 5和Chrome 18的支持。同样,它应该具有像MIT或Apache这样的宽松许可证。

顺便提一下,Google+ 明显使用他们的 Closure 库,但好像涉及到很多手动调整。 - user123444555621
1
你希望它如何处理表单提交或其他非GET请求? - Scott Sauyet
@ScottSauyet 我还没有考虑过那个问题。我猜那是另外一个故事,但如果有一个可以解决那个问题的解决方案,我会非常高兴。 - user123444555621
@Pumbaa80,针对我提供的答案,您可以通过addToHistory(id, data, title, url, options);方法手动添加任何内容到历史记录中。在表单提交期间,使用该AjaxTCR Library API方法来处理这些非缓存历史位置/对象。 - arttronics
你不需要一个库,我相信你提到的其中一个库加上100行JS代码就可以完成工作。 - Guillaume86
7个回答

21
我相信 Sammy.js (http://sammyjs.org)(MIT许可)对于你想做的事情有最好的关注点,其两个主要支柱为:

  1. 路线
  2. 事件

我可以引用文档,但它非常简单:

  • 设置与需要完成的内容相关的客户端路线,例如:通过ajax更新视图

  • 将事件链接到调用路线,例如:当我点击链接时调用上面的路径。(我认为您必须确保在定义的事件中调用e.preventDefault,因为这实际上是应用程序决策,所以不能被任何库抽象化,您将要使用imho)

一些相关文档

路由示例:(来自http://sammyjs.org/docs/tutorials/json_store_1

 this.get('#/', function(context) {
    $.ajax({
      url: 'data/items.json',
      dataType: 'json',
      success: function(items) {
        $.each(items, function(i, item) {
          context.log(item.title, '-', item.artist);
        });
      }
    });
  });

或者类似于这样

 this.get('#/', function(context) {
     context.app.swap(''); ///the 'swap' here indicates a cleaning of the view
                              //before partials are loaded, effectively rerendering the entire screen. NOt doing the swap enables you to do infinite-scrolling / appending style, etc. 
     // ...
   });

当然,其他客户端MVC框架也可以是一个选择,它们可以减少更多的管道,但在这种情况下可能有些过度。这里有一个相当不错(而且还比较新)的比较:

http://codebrief.com/2012/01/the-top-10-javascript-mvc-frameworks-reviewed/

(我自己使用Spine.js)。最后,我认为包含一篇我写过的关于客户端刷新等方面的最佳实践的详细答案可能会很有用。也许你会发现它有用:

Accessibility and all these JavaScript frameworks

2
我已经+1来弥补那个人的过失。现在,点赞是公平的了。话虽如此,由于原帖发布者未能手动授予全部+500,所以看起来你将获得一半的悬赏金。干杯! - arttronics

9

我目前在我的一个应用程序中使用PathJS。 这是我做出的最好的决定。 针对您特定的用例,请查看HTML5示例

使示例工作的代码片段(来自源代码):

<script type="text/javascript">
        // This example makes use of the jQuery library.

        // You can use any methods as actions in PathJS.  You can define them as I do below,
        // assign them to variables, or use anonymous functions.  The choice is yours.
        function notFound(){
            $("#output .content").html("404 Not Found");
            $("#output .content").addClass("error");
        }

        function setPageBackground(){
            $("#output .content").removeClass("error");
        }        

        // Here we define our routes.  You'll notice that I only define three routes, even
        // though there are four links.  Each route has an action assigned to it (via the 
        // `to` method, as well as an `enter` method.  The `enter` method is called before
        // the route is performed, which allows you to do any setup you need (changes classes,
        // performing AJAX calls, adding animations, etc.
        Path.map("/users").to(function(){
            $("#output .content").html("Users");
        }).enter(setPageBackground);

       Path.map("/about").to(function(){
            $("#output .content").html("About");
        }).enter(setPageBackground);

       Path.map("/contact").to(function(){
            $("#output .content").html("Contact");
        }).enter(setPageBackground);

        // The `Path.rescue()` method takes a function as an argument, and will be called when
        // a route is activated that you have not yet defined an action for.  On this example
        // page, you'll notice there is no defined route for the "Unicorns!?" link.  Since no
        // route is defined, it calls this method instead.
        Path.rescue(notFound);

        $(document).ready(function(){
            // This line is used to start the HTML5 PathJS listener.  This will modify the
            // `window.onpopstate` method accordingly, check that HTML5 is supported, and
            // fall back to hashtags if you tell it to.  Calling it with no arguments will
            // cause it to do nothing if HTML5 is not supported
            Path.history.listen();

            // If you would like it to gracefully fallback to Hashtags in the event that HTML5
            // isn't supported, just pass `true` into the method.

            // Path.history.listen(true);

            $("a").click(function(event){
                event.preventDefault();

                // To make use of the HTML5 History API, you need to tell your click events to
                // add to the history stack by calling the `Path.history.pushState` method. This
                // method is analogous to the regular `window.history.pushState` method, but
                // wraps calls to it around the PathJS dispatched.  Conveniently, you'll still have
                // access to any state data you assign to it as if you had manually set it via
                // the standard methods.
                Path.history.pushState({}, "", $(this).attr("href"));
            });
        });
    </script>

PathJS拥有路由库中最受欢迎的一些功能:
  • 轻量级
  • 支持HTML5历史API、'onhashchange'方法和优雅降级
  • 支持根路由、救援方法、参数化路由、可选路由组件(动态路由)和面向切面编程(AOP)
  • 经过充分测试(测试位于./tests目录下)
  • 与所有主流浏览器兼容(已在Firefox 3.6、Firefox 4.0、Firefox 5.0、Chrome 9、Opera 11、IE7、IE8、IE9上测试通过)
  • 独立于所有第三方库,但可以与它们协同工作
我认为这些功能中最吸引人的是最后两点。 你可以在这里找到它们。
希望这对你有所帮助。

1
请评论以帮助我理解为什么您给了我一个负评。 - Sujay
1
我刚刚完成了一个使用pathjs的项目,我非常满意。非常容易使用(并使其与Google Analytics配合工作)。这是一个极简主义库,但它应该有更好的文档。 - Nicolas Le Thierry d'Ennequin

8

我建议组合使用crossroads.js作为路由器http://millermedeiros.github.com/crossroads.js/和hasher用于处理浏览器历史记录和哈希URL(带有许多备选解决方案):https://github.com/millermedeiros/hasher/(基于http://millermedeiros.github.com/js-signals/

这仍然需要几行代码(以加载ajax内容等),但在处理路由时可以给您提供大量其他可能性。

以下是使用jQuery的示例(上述库不需要jQuery,我只是懒...)

http://fiddle.jshell.net/Fe5Kz/2/show/light

HTML

<ul id="menu">
    <li>
        <a href="foo">foo</a>            
    </li>
    <li>
        <a href="bar/baz">bar/baz</a>
    </li>
</ul>

<div id="content"></div>

JS

//register routes
crossroads.addRoute('foo', function() {
    $('#content').html('this could be ajax loaded content or whatever');
});

crossroads.addRoute('bar/{baz}', function(baz) {

    //maybe do something with the parameter ...
    //$('#content').load('ajax_url?baz='+baz, function(){
    //    $('#content').html('bar route called with parameter ' + baz);
    //});

    $('#content').html('bar route called with parameter ' + baz);
});


//setup hash handling
function parseHash(newHash, oldHash) {
    crossroads.parse(newHash);
}
hasher.initialized.add(parseHash);
hasher.changed.add(parseHash);
hasher.init();


//add click listener to menu items
$('#menu li a').on('click', function(e) {
    e.preventDefault();
    $('#menu a').removeClass('active');
    $(this).addClass('active');

    hasher.setHash($(this).attr('href'));
});​

5
您是否已经查看过微软的BigShelf示例单页应用程序?它似乎覆盖了大部分您正在询问的内容。
它使用History.js,一个自定义的包装对象NavHistory轻松控制导航,并使用Knockout.js处理点击事件。
以下是其极简工作流程:首先需要初始化一个NavHistory对象,该对象包装了history.js并注册回调函数,在推送状态或哈希更改时执行:
var nav = new NavHistory({
    params: { page: 1, filter: "all", ... etc ... },
    onNavigate: function (navEntry) {
        // Respond to the incoming sort/page/filter parameters
        // by updating booksDataSource and re-querying the server
    }
});

接下来,您将定义一个或多个Knockout.js视图模型,并使用可以绑定到链接按钮等的命令。
var ViewModel = function (nav) {
  this.search = function () {
    nav.navigate({ page: 2, filter: '', ... }); // JSON object matching the NavHistory params
  };
}

最后,在您的标记中,您将使用Knockout.js将命令绑定到各种元素:
<a data-bind="click: search">...</a>

链接的资源更详细地解释了所有这些工作原理。不幸的是,它不像您正在寻找的单一框架,但您会惊讶于使其工作的简单程度。
还有一件事,根据BigShelf示例,我正在构建的网站完全跨浏览器兼容,包括IE6+,Firefox,Safari(移动和桌面)和Chrome(移动和桌面)。

死链接。新链接在这里: https://www.asp.net/single-page-application/raw-content/samples/bigshelfcoderawcontent - Richard Nalezynski

3

2
虽然ExtJS是一个很棒的框架,但它有以下问题:1)如果仅用于所讨论的问题,则过于复杂;2)尚未实现新的History API;3)相当原始,没有路由框架+像您提到的冗余调用这样的怪癖。 - Dmitry Pashkevich

3
AjaxTCR Library 似乎涵盖了所有方面,并包含我以前从未见过的强大方法。它是在 BSD 许可证 (开源倡议) 下发布的。
例如,这里有五个 AjaxTCR.history(); 方法:

init(onStateChangeCallback, initState);

addToHistory(id, data, title, url, options);

getAll();

getPosition();

enableBackGuard(message, immediate);

上述的 addToHistory(); 有足够的参数来允许网站进行深度哈希链接。
更多关于 .com.cookie().storage().template() 的信息提供了足够的方法来处理任何会话数据要求。 AjaxTCR API 网页 文档详细介绍了丰富的信息,还提供可下载的文档! 状态更新:
该网站还有一个示例网页部分,其中包括可下载的准备使用的.zip 文件,其中包含准备好的前端(客户端)和后端(服务器)项目文件。
特别值得注意的是以下准备好使用的示例:
单向 Cookie
HttpOnly Cookies
历史记录窃取
历史记录浏览器 还有很多其他示例,这些示例完善了使用许多 API 方法的过程,使得任何小的学习曲线都能更快地完成。

1

PJAX是您所描述的过程。

更高级的pjax技术甚至会在用户悬停在链接上时开始预加载内容。

这是一个很好的pjax库。https://github.com/MoOx/pjax

您标记需要在后续请求中更新的容器:

new Pjax({ selectors: ["title", ".my-Header", ".my-Content", ".my-Sidebar"] })

在上面的代码中,只有title.my-header.my-content.my-sidebar会被ajax调用返回的内容替换。
需要注意JS加载和检测页面是否已准备就绪的方法。JavaScript不会在新页面上重新加载。同样出于这个原因,需要注意分析调用何时被调用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接