日期:2014-05-16  浏览次数:20393 次

knockoutjs 单页引用 客户端重定向(location.hash history.pushState)
大部分现代的,相应的,迷人的Web应用已经超越经典的Ajax,变成单页引用(single page applications:)。用户能象使用本地应用一样,在一个界面内完成各种操作,一个很著名的例子就是:GMail。

这些单页引用使用hash-based url 或者 pushState 导航来支持 浏览器的回退、前进、书签操作。(location.hash就是#锚点,一般用在Web页面内部的片段之间进行导航!)

data-bind="text: $data": $data表示引用foreach循环中的数组数据

data-bind="with: chosenFolderData": with表示后面的变量chosenFolderData不是null时,才使用绑定的相关控件。with内部的所有控件绑定变量时,引用就不需要再加chosenFolderData.前缀了,直接使用mails就可以

chosenFolderId, chosenFolderData , chosenMailData 都是被观察对象,在相关操作时给这些变量设置, 然后一些绑定到这些变量的UI会被自动更新。chosenFolderId是被观察者,所有取值、设置都是通过方法的形式做的

client-side navigation客户端重导航,使用hash-based url 或者 pushState技术。Sammy 就是使用hash-based url。

Sammy js client-side navigation的基本技术是采用一层额外的indirection客户端内部定向。没有使用之前,goToFolder 和goToMail 方法都是直接调用Ajax请求然后更新viewmodel 的状态。使用后,goToFolder 和goToMail 方法仅仅触发client-side navigation客户端导航,然后Sammy 包会捕获到客户端导航操作再发出相应的Ajax请求后再更新viewmodel状态。indirection客户端内部定向也就意味着如果用户点击浏览器上面的回退、前进按钮,Sammy会捕获到这些请求,然后viewmodel 会被正常更新

location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,用location.href=url就可以直接将页面重定向url。一个完整的URL地址的格式为:协议://主机:端口/路径名称#hash标识?搜索条件
location.hash则可以用来获取或设置页面的标签值。比如http://domain/#admin的location.hash="#admin"。

<!-- Todo: Create UI -->
<script src="/scripts/lib/sammy.js" type="text/javascript"></script>

<!-- Folders -->
<ul class="folders" data-bind="foreach: folders">
    <li data-bind="text: $data,
               css: { selected: $data == $root.chosenFolderId() },
               click: $root.goToFolder"></li>
</ul>

<!-- Mails grid -->
<table class="mails" data-bind="with: chosenFolderData">
    <thead><tr><th>From</th><th>To</th><th>Subject</th><th>Date</th></tr></thead>
    <tbody data-bind="foreach: mails">
        <tr data-bind="click: $root.goToMail">
            <td data-bind="text: from"></td>
            <td data-bind="text: to"></td>
            <td data-bind="text: subject"></td>
            <td data-bind="text: date"></td>
        </tr>     
    </tbody>
</table>


<!-- Chosen mail -->
<div class="viewMail" data-bind="with: chosenMailData">
    <div class="mailInfo">
        <h1 data-bind="text: subject"></h1>
        <p><label>From</label>: <span data-bind="text: from"></span></p>
        <p><label>To</label>: <span data-bind="text: to"></span></p>
        <p><label>Date</label>: <span data-bind="text: date"></span></p>
    </div>
    <p class="message" data-bind="html: messageContent" />
</div>

function WebmailViewModel() {
    // Data
    var self = this;
    self.folders = ['Inbox', 'Archive', 'Sent', 'Spam'];
    self.chosenFolderId = ko.observable();
    self.chosenFolderData = ko.observable();
    self.chosenMailData = ko.observable();

    // Behaviours
    //self.goToFolder = function(folder) {
    //    self.chosenFolderId(folder);
    //    self.chosenMailData(null); // Stop showing a mail
    //    $.get('/mail', { folder: folder }, self.chosenFolderData);
    //};
    //self.goToMail = function(mail) {
    //    self.chosenFolderId(mail.folder);
    //    self.chosenFolderData(null); // Stop showing a folder
    //    $.get("/mail", { mailId: mail.id }, self.chosenMailData);
    //};
    self.goToFolder = function(folder) { location.hash = folder };
    self.goToMail = function(mail) { location.hash = mail.folder + '/' + mail.id };