define({ title: 'SPA - 为WebApp设计的路由控制和视图转换框架', body: '\
\
\ \

SPA是为构建WebApp设计的路由控制和视图转换框架

\

\ SPA专注于解决构建WebApp时遇到的共性问题,尤其适用于构建MobileApp,\ 我们和jQuery MobileSencha Touch等框架不同,\ 并不是一个构建移动端应用的前端整体解决方案,所以我们不包含UI组件,如果你不想自己设计界面,\ 可以用BootstrapTopcoat等UI Components框架配合SPA,\ 来快速构建你的WebApp;\

\

\ SPA依赖ZeptojQuery,\ 并且每个视图可以通过RequireJSSea.js等CommonJS解决方案或者自定义的方式进行模块化组织、异步加载;\

\

\ SPA支持移动端和桌面端的现代浏览器;\

\

\ \ \ \

\

\ \ \ \

\ \

提供快速的开发实现

\

\ 你可以像开发传统网站一样,先设计并制作每个视图,比如页面、导航、对话框等等,然后通过SPA提供的接口把每个视图拼装组织,完成一个拥有NativeApp体验的WebApp;\

\

保留更大的设计自由度

\

\ SPA相对于jQuery Mobile和Sencha Touch等框架,SPA是非常轻量级的,我们只关心并解决WebApp的路由控制和视图转换等共性问题,每个场景被模拟成一个<body>节点,场景内的具体界面和交互设计完全交给开发者;\

\

减少后端依赖

\

\ 视图的渲染和路由是在前端完成的,后端只需要提供一个简单的入口页面(Single-page application)和应用所需的异步数据接口;如果再配合使用javascript模版,还可以最大化的利用前端缓存,减少网络流量请求;\

\

事件驱动

\

\ SPA不提供类、对象或函数库,利用jQuery的自定义事件和事件代理,SPA的接口都绑定到DOM上,所有的操作都是触发相关DOM上的自定义事件,将各个视图的代码解耦隔离,降低开发复杂度,这个特性和Flight框架一致。\

\ \

\ WebApp中不同的视图(页面)通常需要被链接、收藏或分享,所以需要记录视图的地址,SPA提供基于hash fragments的URL路由控制,每个路由规则都绑定相应的页面视图,应用启动后将根据路由的变化自动转换视图;\

\

\ SPA中的hash路由被设计成基于字符串片段的规则,每个片段用斜线/分割,\ 片段可以包含以冒号为前缀的参数:param,\ 以星号为前缀*splat可以匹配任意数量的片段,\ 括号括起来(:optional)可以匹配可选的片段(有或者无),\ 路由规则还可以直接用正则表达式创建;\

\

\ 解析后的请求参数数组requestData会存储到对应的页面视图对象中,以提供给视图渲染、视图初始化等回调函数所使用;\

\

\ 比如:\

\

\ 规则"search/:keyword/page:num"可以匹配路由请求#search/something/page2,并将参数"something""2"存储到视图对象;\

\

\ 规则"file/*path"可以匹配路由请求#file/some/folder/file.txt,并将参数"some/folder/file.txt"存储到视图对象;\

\

\ 规则"docs/:section(/:subsection)"可以匹配路由请求#docs/faq#docs/faq/install,并将参数"faq"存储到第一个视图对象,将参数"faq""install"存储到第二个视图对象;\

\

\ 规则"/^(.*?)\/open$/"可以匹配路由请求#some/thing/open,并将参数"some/thing"存储到视图对象;\

\

\ 当用户点击链接、浏览器后退按钮或者输入url hash进行路由请求时,window的popstate事件将被触发,然后寻找匹配的路由规则,创建对应的页面视图,并进行初始化,再通过对应的转换动画来显示;\

\
\
var pageHome = {\n\
  route: "",\n\
  classname: "home",\n\
  animate: "fadeIn",\n\
  view: function() {\n\
    var $page = this\n\
    requirejs(["home"], function(viewData) {\n\
      $doc.trigger("spa:initview", [$page, viewData])\n\
    })\n\
  }\n\
}\n\
\n\
$doc.trigger("spa:route", [pageHome])\n\
      
\

\ 除了用户操作,还可以通过spa:navigate事件主动进行路由请求。\

\
\
$doc.trigger("spa:navigate", {\n\
  hash: "go/to/some/pageview"\n\
})\
      
\ \

\ SPA提供了两类视图,分别是页面视图面板视图,不同视图之间通过设定的动画规则进行转换;\

\

页面视图

\

\ 页面视图需要绑定路由规则,每次路由请求都会寻找匹配的路由规则,然后激活对应的页面视图;\

\

\ 页面视图是用一组<div>节点构造的容器,模拟成<body>节点承载视图内容并覆盖整个视图区域;\ .spa-page-customclassname区分不同视图,添加自定义样式;\ 每个节点的背景色默认透明;\ .spa-page-body节点用来承载内容,通常在不需要半透明背景色的视图中,视图的背景色应该设置到该节点;\

\
\
<!--页面视图的DOM结构-->\n\
<div class="spa-page spa-page-customclassname">\n\
  <div class="spa-page-body">\n\
    <!--视图内容会被渲染到这里-->\n\
  </div>\n\
</div>\
      
\

\ 打开新页面视图\

\
\
//demo:打开新页面视图\n\
var demoNewPage = {\n\
  route: "demo/newpage",\n\
  classname: "demo-newpage",\n\
  animate: "pushInLeft",\n\
  view: function() {\n\
    var $page = this\n\
    requirejs(["demo.newpage"], function(viewData) {\n\
      $doc.trigger("spa:initview", [$page, viewData])\n\
    })\n\
  }\n\
}\n\
\n\
$doc.trigger("spa:route", [demoNewPage])\n\
      
\

面板视图

\

\ 面板视图不需要绑定路由规则,即没有对应的路由请求,需要在javascript中主动打开,面板视图可以用来做侧边栏菜单、对话框等应用组件;\

\
\
//打开面板\n\
$doc.trigger("spa:openpanel", [panelid, pushData])\n\
      
\

\ 面板视图的容器结构是在页面视图容器结构的基础上进行扩展;\ 增加了节点id#spa-panel-panelid和classname.spa-panel;\

\
\
<!--面板视图的DOM结构-->\n\
<div id="spa-panel-panelid" class="spa-page spa-panel spa-page-customclassname">\n\
  <div class="spa-page-bg"></div>\n\
  <div class="spa-page-body">\n\
    <!--视图内容会被渲染到这里-->\n\
  </div>\n\
</div>\
      
\

\ 侧边栏菜单\ 提示对话框\ 确认对话框\

\
\
//demo:侧边栏菜单\n\
var demoPanelSidemenu = {\n\
  id: "demoPanelSidemenu",\n\
  classname: "demo-panel-sidemenu",\n\
  animate: "revealInRight",\n\
  view: function() {\n\
    var $panel = this\n\
    requirejs(["demo.panelsidemenu"], function(viewData) {\n\
      $panel.trigger("spa:initpanel", viewData)\n\
    })\n\
  }\n\
}\n\
\n\
//demo:提示对话框\n\
var demoPanelAlert = {\n\
  id: "demoPanelAlert",\n\
  classname: "demo-panel-alert",\n\
  animate: "zoomIn",\n\
  view: function() {\n\
    var $panel = this\n\
    requirejs(["demo.panelalert"], function(viewData) {\n\
      $panel.trigger("spa:initpanel", viewData)\n\
    })\n\
  }\n\
}\n\
\n\
//demo:确认对话框\n\
var demoPanelConfirm = {\n\
  id: "demoPanelConfirm",\n\
  classname: "demo-panel-confirm",\n\
  animate: "overlayInUp",\n\
  view: function() {\n\
    var $panel = this\n\
    requirejs(["demo.panelconfirm"], function(viewData) {\n\
      $panel.trigger("spa:initpanel", viewData)\n\
    })\n\
  }\n\
}\n\
\n\
//添加面板\n\
$doc.trigger("spa:panel", [demoPanelSidemenu, demoPanelAlert, demoPanelConfirm])\n\
\n\
//点击按钮打开面板\n\
$doc.trigger("spa:openpanel", [panelid]) //panelid = demoPanelSidemenu, demoPanelAlert, demoPanelConfirm\n\
      
\

转换动画

\

\ SPA内置了22组视图转换动画,每组包含一个入场动画和一个相反的出场动画,比如fadeIn & fadeOutpushInRight & pushOutLeft,\ 其中有12组动画是只支持面板视图,比如overlayInUp & overlayOutDown,\

\

\ 可以通过$doc.trigger("spa:addTransitPageAnimates", Animates)添加自定义的视图转换动画;\

\

\ 如果视图通过异步加载,将触发遮罩层和loading动画(可自定义);\

\

页面视图转换动画

\

\ default\

\

\ fadeIn\ fadeOut\

\

\ slideInLeft\ slideOutRight\

\

\ slideInRight\ slideOutLeft\

\

\ slideInUp\ slideOutDown\

\

\ slideInDown\ slideOutUp\

\

\ pushInLeft\ pushOutRight\

\

\ pushInRight\ pushOutLeft\

\

\ pushInUp\ pushOutDown\

\

\ pushInDown\ pushOutUp\

\

\ zoomIn\ zoomOut\

\

面板视图转换动画

\

注意:面板视图只支持正向的入场动画

\

\ overlayInLeft\ overlayOutRight\

\

\ overlayInRight\ overlayOutLeft\

\

\ overlayInUp\ overlayOutDown\

\

\ overlayInDown\ overlayOutUp\

\

\ revealInLeft\ revealOutRight\

\

\ revealInRight\ revealOutLeft\

\

\ revealInUp\ revealOutDown\

\

\ revealInDown\ revealOutUp\

\

\ pushPartInLeft\ pushPartOutRight\

\

\ pushPartInRight\ pushPartOutLeft\

\

\ pushPartInUp\ pushPartOutDown\

\

\ pushPartInDown\ pushPartOutUp\

\ \

\ spa wiki\

\ \

\ SPA遵循MIT协议,无论个人还是公司,都可以免费自由使用。\

\
\
\ ', init: function(pageData) { var $view = this // 获取hash function getHash(url) { url = url || location.href return url.replace(/^[^#]*#?\/?(.*)\/?$/, '$1') } $('pre', $view).each(function(i, e) { hljs.highlightBlock(e) }) $view.on('click', '.btn-demo-panel', function(event) { event.preventDefault() var $btn = $(this), panelid = $btn.attr('data-panel') $doc.trigger('spa:openpanel', [panelid]) }) $view.on('click', '.btn-transitpage', function(event) { event.preventDefault() var $btn = $(this), animate = $btn.attr('data-animate'), hash = getHash($btn.attr('href')) $doc.trigger('spa:navigate', {hash: hash, pushData: {animate: animate}}) }) $view.on('click', '.btn-transitpanel', function(event) { event.preventDefault() var $btn = $(this), animate = $btn.attr('data-animate') $doc.trigger('spa:openpanel', ['demoPanelTransit', {animate: animate}]) }) $('.page-container-navbar', $view).trigger('spa:scroll') } })