聊聊单页应用

单页应用优势

单页应用有几个优势:

  • 可以像桌面应用一样渲染,只需要重绘界面上需要变化的部分。
  • 可以拥有和桌面一样的响应速度,尽可能把工作数据和处理过程从服务器端转移到客户端,把响应时间缩减至最小。只有数据验证、授权和持久存储必须放在服务器端。
  • 可以把它的状态通知给用户,当单页应用需要等待服务器响应的时候,可以动态显示进度条或者繁忙指示器。

Pjax

Pjax是一个优秀的单页应用路由解决方案,Pjax的原理十分简单。

  • 拦截a标签的默认跳转动作。
  • 使用Ajax请求新页面。
  • 将返回的Html替换到页面中。
  • 使用HTML5的History API或者Url的Hash修改Url。

我们来看看HTML5在History里增加了什么:

    history.pushState(state, title, url)

pushState方法会将当前的url添加到历史记录中,然后修改当前url为新url。请注意,这个方法只会修改地址栏的Url显示,但并不会发出任何请求。我们正是基于此特性来实现Pjax。它有3个参数:

  • state: 可以放任意你想放的数据,它将附加到新url上,作为该页面信息的一个补充。
  • title: 顾名思义,就是document.title。不过这个参数目前并无作用,浏览器目前会选择忽略它。
  • url: 新url,也就是你要显示在地址栏上的url。
history.replaceState(state, title, url)

replaceState方法与pushState大同小异,区别只在于pushState会将当前url添加到历史记录,之后再修改url,而replaceState只是修改url,不添加历史记录。
window.onpopstate 事件
一般来说,每当url变动时,popstate事件都会被触发。但若是调用pushState来修改url,该事件则不会触发,因此,我们可以把它用作浏览器的前进后退事件。该事件有一个参数,就是上文pushState方法的第一个参数state。
IE6到IE9是不支持pushState的,要修改Url,只能利用Url的Hash,也即是#号。那么接下来我们简单实现一下#号的路由跳转:

    //default page, which will be loaded initially
    var home = {}; 
    home.partial = 'lib/home.html';
    home.init = function(){
    }
    //404 page
    var notfound = {};
    notfound.partial = "lib/404.html";
    notfound.init = function(){
        alert('URL does not exist. please check your code.');
    }

所有的公用方法打包放到一个对象miniSPA中

    var miniSPA = {};

然后是 changeUrl 方法,对应在index.html中有如下触发定义:

    <body onhashchange="miniSPA.changeUrl();">

onhashchange是在location.hash发生改变的时候触发的事件,能够通过它获取局部 url 的改变。在index.html中定义了如下的链接:

    <a href="#home">Home (Default)</a>
    <a href="#postMD">POST request</a>
    <a href="#getEmoji">GET request</a>
    <a href="#wrong">Invalid url</a>
    <!-- html 片段嵌入的位置 -->
    <div id="demo"></div>

每个 url 都以#号开头,这样就能被onhashchange事件抓取到。最后的 div 就是局部刷新的 html 片段嵌入的位置。

   miniSPA.changeUrl = function(){
       var url = location.hash.replace('#', '');
       if(url === '') url = 'home';
       //刚才定义的路由对象都是全局变量
       if(!window[url]){
           url = 'notfound';
       }
       //请求相应页面的数据
       miniSPA.ajaxRequest(window[url].partial, 'GET', '', function(status, page){
           if(status === '404'){
               //html片段嵌入id为demo的div中,这里setting的具体实现就不定义了,其实是通过ajax数据改变前端的model,然后填充视图模板改变视图
               settings.divDemo.innerHTML = page404;
           } else {
               settings.divDemo.innerHTML = page;
           }
           miniSPA.initFunc(url);  
       })
   }

initFunc方法的作用是解析片段对应的初始化方法,判断其类型是否为函数,并执行它。

    //execute the controller function responsible for current template
    miniSPA.initFunc = function(partial) {               
        var fn = window[partial].init;
        if(typeof fn === 'function') {
            fn();
        }
    }

当然,点击浏览器的后退、前进,就要通过window.onpopstate 事件来处理了。

MVC

上面谈到的Pjax,承担着应用范围的任务,像是管理URL锚或者cookie,把特定功能的任务调度隔离的功能模块。接下来我们来回忆一下MVC模式,MVC的部件包括以下三个:

  • 模型,应用的数据和业务规则
  • 视图,模型数据的感官(通常是视觉的)表现
  • 控制器,将用户的请求转化成命令,更新应用的模型和视图。

我们的单页面应用架构在多个层级上采用重复的MVC模式,所以我们把它叫做分形MVC。我们理解的分形有多深,和看问题的角度有关,从远处看我们的web应用的时候,看到的是单一的MVC模式,控制器处理URI和用户输入,与模型进行交互,在浏览器中提供视图。
当放大一点时,应用被分割成两部分,服务器端采用MVC模式向客户端提供数据,采用MVC的单页应用允许用户查看浏览器端的模型,并与之交互。服务器端的模型是从数据库获取的数据,而视图是要发送给浏览器的数据表现,控制器是协调数据管理和同浏览器通信的代码。在客户端,模型包括从服务器接收到的数据,视图是用户界面,控制器是协调客户端数据和界面的逻辑。
再放大得近一点,我们看到了更多的MVC模式。比如,服务器端应该采用MVC模式来提供HTTP数据API。服务器端使用的数据库采用它自己的MVC模式,在客户端,客户端应用使用MVC模式,客户端组件调用的子功能模块本身也使用MVC模式。

构建Model

Model把所有的业务逻辑和数据整合到一个名字空间里面,Pjax或者功能模块并不直接和web服务器通信,而是通过和Model交互,Model通过自己使用Data模块,从web服务器分离出来。假设我们实现一个聊天的chat功能和登入登出,那么会遇到以下问题:

  • 为了管理登入和登出的问题,Pjax需要知道当前用户,它需要确定“当前用户是谁”的方法,在需要时更改用户。
  • chat功能也需要查看当前用户,以此判断他是否授权发送或接收消息。它需要确定正在和用户聊天的人,如果有的话。它需要查询在线人员的列表,这样就可以把他们显示在聊天模块的左边。

模块很多必须的业务逻辑和数据是重叠的。比如,Pjax和chat功能都需要知道当前用户对象,我们想到了一些策略,如何设法管理这种重叠:

  • 在每个功能模块中构建必须的逻辑和数据
  • 在不同的功能模块中构建部分逻辑和数据,然后在模块中互相调用,以便共享信息。
  • 构建中央Model,合并逻辑和数据。

第一种方法显然不妥,在不同的模块中维护并行的数据和方法,这容易产生冗余代码和错误。第二种选择的效果好一点,不过是暂时的,一旦逻辑和数据达到了适度水平的复杂度。第三个选择是使用Model,这是目前为止最好的选择。
Model是Pjax和所有功能模块访问单页应用的数据(准确说是共享数据)和业务逻辑的地方。如果需要登入,我们调用Model提供的方法,如果想获取人员列表,就从Model获取。虽然所有的业务逻辑和数据都是通过Model访问的,但并不意味着必须只能使用一个js文件来存放Model,可以通过命名空间把Model分成多个容易管理的小文件。
Model不需要浏览器,这意味着Model不可以假定存在document对象,让Pjax和功能模块来表示Model的数据,是“干净的”MVC。通过避免DOM,我们可以测试除了UI之外的所有东西,无需运行浏览器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值