App.js 的目标是为移动webapp提供一个健壮良好的开端,处理常见的功能并且兼容其他流行的JS库。
入门
请确保您在开始学习之前已经下载了App.js的文件包。它将是您开始构建您的app的第一步。
将下载的代码包解压并打开 index.html
下面是一个App.js页面的标准格式:
<!DOCTYPE html><html><head><title>My App</title><metaname="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui"><linkrel="stylesheet"href="//cdn.kik.com/app/2.0.1/app.min.css"><style>/* put your styles here */</style></head><body><!-- put your pages here --><scriptsrc="//zeptojs.com/zepto.min.js"></script><scriptsrc="//cdn.kik.com/app/2.0.1/app.min.js"></script><script>/* put your javascript here */</script></body></html>
注意:
app.min.js
是核心模块,它包含了所有的功能的定义。app.min.css
是 App.js 提供的默认的样式. 它包含 iOS/Android 下所有组件的样式.zepto.min.js
是一个类JQuery的专注轻量级、对移动设备友好的JS库。它并不是App.js所必需的,但是拥有它将更好的写简练且跨平台的代码。您可以用JQuery替代它。- 名为viewport的meta标签是为了确保应用无论运行在哪种设备上window大小都进行了正确的调整。这对于在不同平台之间保持窗口大小缩放级别一致很重要。
- 注释中”pages” 部分就是您插入HTML页面元素的地方,就像 the UI section.中描述的一样
- 注释中 “javascript” 部分是您插入controllers and navigation 代码的地方.
- 注释中”styles” 部分是您插入样式代码的地方.
因为 App.js apps 根本就是静态的,您需要做的就是将代码放在一个HTML文件中然后在浏览器中打开它来测试.
建议您通过Chrome浏览器并模拟一个您指定的移动平台来运行您的app. 不要忘记打开 touch event emulation.
图片搜索app示例
您也可以选择先下载此项目的代码并以此开始您的学习。
页面(Pages)
App.js 是为创建静态的单页面而生的。这意味着它让所有页面的跳转都保持在一个网页内。将”pages” 定义为DOM节点就可以实例化了。
页面是包含像顶部和内容区这种常用组件的HTML元素。请注意尽管这些组件是可选的,但是”app-page”的是您的HTML所必需的。
<divclass="app-page"data-page="home"><divclass="app-topbar"></div><divclass="app-content"></div></div>
“app-page” 必须有一个”data-page” 来为这个页面命名. 这个名字用来确保JS代码在任何时候都能加载这个页面。
App.load('home');
这一行JS代码用来命令App.js加载名字为”home”的页面. 更深入的说,App.js 将”app-page”元素同它的名字一致克隆并将它显示给用户。这样,我们就可以为这个页面创建多个实例并在不同的环境中使用它。
Under the hood
控制器(Controllers)
无论何时通过”App.load”加载一个新页面,一个特定的函数就会被执行以准备你的HTML页面中的 app-page 元素。这允许您在页面被显示给用户之前处理其中的动态内容(This allows you to hook up any dynamic aspects of your page prior to it being shown to the user. )。例如,按钮可以被绑定上任何你希望它做的动作。
/* in your javascript */App.controller('home',function(page){// this runs whenever a 'home' page is loaded// 'page' is the HTML app-page element $(page).find('.app-button').on('click',function(){ console.log('button was clicked!');});});
控制器也可以是一个类,这样您就可以让控制器之间相互继承。
functionHomeController(page){// this runs whenever a 'home' page is loadedthis.foo ='bar';this.print();}HomeController.prototype.print=function(){ console.log(this.foo);};App.controller('home',HomeController);
如果您在HTML中定义了多个页面,您可能需要为每一个页面定义一个控制器以便它们被加载的时候能够被正确的构造。
页面参数(Page Arguments)
控制器可以有动态的参数,因此您就可以根据输入的参数来构造出稍有不同的页面。例如,如果我正在开发一个联系人列表的app,我就会为列表中的每一个联系人加载一个详细页面。我们需要做的就是生成一个包含空白内容的通用联系人页面,然后根据是要加载哪个具体联系人来填充内容。
要加载一个带动态参数的页面,您只要在调用”App.load”的时候添加一个JSON对象,然后您就可以使用这个对象。
To load a page with special arguments simply add an JSON object to the “App.load” call and you’ll be able to use that object in the populator accordingly.
<!-- in your html --><divclass="app-page"data-page="contact"><divclass="app-topbar"><divclass="app-title">Contact</div></div><divclass="app-content"><divclass="first-name"></div><divclass="last-name"></div></div></div>
/* in your javascript */App.controller('contact',function(page, contact){ $(page).find('.first-name').text(contact.firstName); $(page).find('.last-name').text(contact.lastName );});
/* somewhere else in javascript */var contact ={ firstName :'Bruce', lastName :'Lee'};App.load('contact', contact);
在上面第二段代码的最后一行,”contact”对象可以设置任何的姓名,控制器会将其相应的显示到页面。
In the second last line of code above, the “contact” object could have any first / last name and the controller would setup the page accordingly.
这些和Zepto or jQuery结合的参数通过有效的绑定可以让您通过模版的方式加载HTML页面。
These kinds of arguments coupled with Zepto or jQuery combine to effectively let you template your HTML.
事件(Events)
下面是一些App.js会触发的事件:
There are various events that App.js will fire on your page.
- “appLayout”:这可能是最重要的一个事件,它会在屏幕方向改变、窗口大小调整、DOM中的页面布局或者其他任何影响页面布局的时候触发。绑定这个事件并将任何布局相关的代码放在这里。
- “appShow”: 当用户跳转到此页面的时候触发。如果用户跳转到其他页面然后返回将可能导致这个事件被多次触发。
- “appHide”: 当用户跳转出此页面的时候触发。请注意这并不一定意味着页面会被销毁,例如,当用户浏览到其它页面他也很可能会退回来。
- “appBack”: 当用户退回到页面栈中前一个页面的时候触发。
- “appForward”:当用户前进到页面栈中后一个页面的时候触发。 Fired when the user is navigating forward in the stack from this page.
- “appBeforeBack”: 当页面即将回退的时候触发。可以通过在handler中对事件返回
false
来取消此次页面跳转(原理和window.onbeforeunload
类似)。 - “appReady”: 这个事件会在
appShow
第一次调用的时候触发。它有额外的机制来保证window.onload
被调用,这使得在绑定网络初始化代码的时候更加有效。It has the added bonus on ensuring thatwindow.onload
has been called, making it useful for network-bound initialization code. - “appDestroy”: 当页面实例即将被完全销毁的时候触发。这一般发生在用户已经完全从这个页面回退了的时候。
为了处理这些事件,将他们绑定到你的控制器中。
App.controller('home',function(page){ $(page).on('appShow',function(){ console.log('the user can see it!');});});
此外,所有的事件都可以通过控制器类的方法的方式来绑定。
functionHomeController(page){// stuff}HomeController.prototype.onShow =function(){ console.log('the user can see it');};App.controller('home',HomeController);
在销毁页面的时候,很重要的一点是取消绑定页面范围之外绑定的事件,以便JavaScript垃圾回收器能够工作。
App.controller('home',function(page){function doStuff(){// handle event} window.addEventListener('keypress', doStuff); $(page).on('appDestroy',function(){ window.removeEventListener('keypress', doStuff);});});
页面栈
就像此前讨论的,App.load
会复制选择的页面,然后为其创建一个控制器最后将其显示给用户。As discussed earlier, App.load
makes clone of the chosen page, creates a controller for it, and then presents it to the user.
App.load('home');
您可以在代码中的任意位置任意时间下调用。如果一个跳转试图执行但是当前页面还正在加载,这个跳转就会加入到跳转队列中然后在当前页面加载完成后再执行。
此外,您还可以添加一个回调函数以便页面跳转完成后执行。
App.load('home',function(){// done!});
因为按钮经常被用来直接加载页面,所以这里也提供了一个快速简洁的方法来做这件事情。
<!-- in your html --><divclass="app-button"data-target="page2">Go to page 2</div><divclass="app-button"data-target="contact"data-target-args='{"firstName":"Bruce","lastName":"Lee"}'>Open contact</div>
当第一个按钮按下的时候 App.load('page2')
会自动的被调用。第二个按钮是类似的,但是它传递了一个页面参数 (App.load('contact', { firstName : 'Bruce' , lastName : 'Lee' })
)
回退栈(Back stack)
当你从一个页面跳转到另外一个页面的时候,App.js 维持了一个页面记录,以便你能够回退到那些页面。App.back
是与App.load
相反的概念,通过它可以回退到前一个页面(当跳转完成后当前页面会被销毁)。
// 加载 home页面// 加载 page2App.back(function(){// 回退到Home页面// page2的appDestroy 事件会被触发});
如果没有可以回退的页面 App.back
会返回 false
.
正如 data-target
特性一样,App.back
中有一个相反的概念.
<!-- in your html --><divclass="app-button"data-back>Go back</div>
当上面的按钮被按下的时候 App.back()
会被自动的调用。
但是回退到一个指定的页面比只是简单的回退一个页面更加常见。
// 加载 home页面// 加载 page2页面// 加载 page3页面App.back('home',function(){// 回到 home页面// page2和 and page3的 appDestroy 事件会被触发});
另一个常见的事是以前一个页面来为其命名。 App.js 提供了一个便利的方法-自动用其即将跳转到的页面来为此回退按钮命名。
<divclass="app-button"data-back="true"data-autotitle></div>
Pick a page
对页面来说,一个常见的用例是为用户提供一个从即将调用的页面中取出一些内容或者数据的方法 。这可以通过下面的方式实现:
App.controller('selector',function(page, request){// 'request' 是从requestor中取来的数据// 返回给调用者//会隐含的调用 App.back 并且销毁页面this.reply({ some:'data'});});App.pick('selector',{ some:'data'},function(data){// 'data'是返回值 is the reply data// 可以用它来:});
转场效果(Transitions)
默认的,App.js会尝试选择最适合你App运行平台的转场效果来切换页面。
App.js 提供了许多的内置转场效果,每一种都可以通过调用”App.load”来指定直接使用哪种效果(覆盖默认的)。
App.load('home','fade');// 在页面之间使用淡入淡出效果
转场效果由 Swapper.js 提供,你可以去文档 查看有哪些可用的效果。
你可以为所有的页面切换设置全局的默认转场效果。
App.setDefaultTransition('transition-name');// 全局的// 根据平台分别设置App.setDefaultTransition({ ios :'transition-name',// iOS iosFallback :'transition-name',// iOS <5 android :'transition-name',// Android androidFallback :'transition-name',// Android < 4 fallback :'transition-name'// non-iOS, non-Android});
也可以分别为每个页面设置默认的转场效果。
App.controller('page2',function(page){this.transition ='fade';});
堆栈恢复(Restore stack)
因为 App.js 对页面栈以及如何构建页面是有意识的,所以它也提供了自动恢复用户的session到最后一次关闭的状态的能力。要使用这个特性需要按照以下方式:
// in your apps main methodtry{// 尝试恢复之前的sessionApp.restore();}catch(err){// else start from scratchApp.load('home');}
下面是仅仅恢复5分钟前的session的例子:
try{App.restore({ maxAge:5*60*1000});}catch(err){App.load('home');}
注意: maxAge
的单位是微秒
单页面也可以阻止恢复行为(for example if they are modal).
App.controller('page2',function(page){this.restorable =false;});
page2
和其它在页面栈中之后的页面将不能被恢复(但是之前的页面可以恢复).
页面栈修改(Stack manipulation)
尽管通常这是一个不好的做法,但是也经常在没有用户交互的情况下内部修改页面栈。
注意不可能在不事先使用App.back
或者 App.load
的情况下删除当前可见的页面。
// stack = [home, page2, page3]App.removeFromStack(0,1);// 从页面栈中删除home页面// stack = [page2, page3]App.addToStack(1,['home','page4']);// 在索引为1的位置添加两个页面// stack = [page2, home, page4, page3]
UI 组件
App.js的页面一般是由一个顶部导航条和内容区域组成的。顶部导航条包含了标题和按钮(用于页面跳转或者其他的行为),内容区域包含了页面实际的元素
<divclass="app-page"><divclass="app-topbar"></div><divclass="app-content"></div></div>
App.js自带了许多已经写好样式的UI 组件。它们大部分都会在app-content
.中结束。 Almost all of these will end up in your app-content
.
注意你可以很方便的为不同的平台设置指定的样式。例如,这个页面的顶部条在iOS和Android中有一点的不同。为了适应这一点,App.js 给body添加了一个与你的app正在运行的平台一致的class,利用这个class你可以将其作为一个选择过滤器来为单独的小部件(widget)设置样式。
.my-widget {/* cross-platform styles */}.app-ios .my-widget {/* ios only styles */}.app-android .my-widget {/* android only styles */}
顶部条
像许多的原生手机应用一样,App.js 的顶部条也包含了一个按钮和标题。
<divclass="app-topbar"><divclass="app-title">页面标题</div></div>
查看按钮部分 了解如何在顶部条中添加按钮。
主体内容
app-content
部分包含了页面所有的内容,包括按钮,列表,输入框。 While these widgets are useful, it is usually the case that the custom HTML structures of the app will go here.
content默认会添加滚动效果,你可以通过添加属性data-no-scroll
来关闭content部分的自动滚动。
按钮
按钮对于任何app都是基本的组件,App.js 有一系列的内置的方便的方法来处理它。任何带有app-button
样式的元素都会被自动的添加相应的样式,并且当按钮按下的时候会添加适当的下沉效果来迅速的响应点击。
<divclass="app-button">My button</div>
在顶部条中的按钮是一个更加常见的位置:
<divclass="app-topbar"><divclass="app-button left"data-back>Back</div><divclass="app-title">Page title</div><divclass="app-button right">Forward</div></div>
像你自己在脑海中模拟的一样,回退按钮会显示在左边,前进按钮会显示在右边。你可能注意到这里的按钮的样式和content区域中的按钮明显有一点不一样,这是因为我们希望这里的按钮更小一些以便适应导航条。
列表 & 滚动
列表很适合用来显示批量的数据,或者是一组选择。
<!-- in your app-content --><ulclass="app-list"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>
此外,列表的元素还可以是按钮。
<ulclass="app-list"><liclass="app-button">Button item</li></ul>
列表元素通常带有label标签将元素分为不同的逻辑区域。
<ulclass="app-list"><label>Animals</label><li>Dogs</li><li>Cats</li><label>Fruits</label><li>Apples</li><li>Oranges</li></ul>
无限滚动(Infinite scroll)
列表通常根据用户的滚动来动态的加载内容。Lists often create the need for dynamically loaded content as the user scrolls. App.js can handle this for you.
App.infiniteScroll(listElement,function(next){// dynamically fetch datavar list =[];// add html elements to listnext(list);});
注意listElement
是那个新元素将被动态的插入的HTML元素。
在正在动态内容被加载的时候显示一个loading界面是一个常见的做法。
App.infiniteScroll(listElement,{ loading: loadingElem },function(next){next([ stuff ]);});
注意loadingElem
会被复制并且当获取新数据的时候在列表底部使用
输入框
App.js 提供了标准的组件来收集用户的输入,无论是HTML inputs 还是 textareas,可以很容易的构建出表单。
<!-- in your app-content --><inputclass="app-input"><inputtype="search"class="app-input"><!-- will have search icon --><textareaclass="app-input"></textarea>
区域(Sections)
“Sections”是App.js UI中的概念,它可以让你将组件块分为不同的区域。
在你的app中的content里试试吧:
<!-- in your app-content --><divclass="app-section"><inputclass="app-input"placeholder="Subject"><textareaclass="app-input"placeholder="Message"></textarea><divclass="app-button">Send</div></div>
你会注意到这些相似的输入框现在都被放到了一个圆角的区域中。 相应的,在区域(section)的四周添加了外边距和合适的边框。这个特性可以应用到任何页面内容(content)部分中的小部件(widgets)上。
<ulclass="app-list app-section"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul><divclass="app-section"><divclass="app-button">Send</div></div><divclass="app-section"><inputclass="app-input"></div>
注意你可以添加任何多的widgets到sestion中,它都会相应的处理。
对话框
App.js 对话框和传统的对话框有一样的功能和动作。它们可以将带有按钮的文字(或者HTML元素)的模块展示给用户来选择或者驳回(dismiss)。

App.dialog({ title :'Network Error', text :'Looks like the connection is flaky. Try again in a bit' okButton :'Try Again', cancelButton :'Cancel'},function(tryAgain){if(tryAgain){// try again}});
另一个案例是从一系列选择项中选择:
App.dialog({ title :'Pick a Fruit', orangeButton :'Orange', redButton :'Apple', greenButton :'Kiwi',},function(choice){switch(choice){case'orange':// do somethingbreak;case'red':// do somethingbreak;case'green':// do somethingbreak;}});
注意你可以为按钮设置任何自定义的名字。
图片预览(PhotoViewer)
PhotoViewer.js 是为 App.js 自定义的可简单设置的图像预览器,它可以像原生的感觉一样运用滑动手势切换图片、双指操作放大缩小。

<!-- in your html --><scriptsrc="http://cdn.kik.com/photo-viewer/1/photo-viewer.js"></script><divclass="app-page dark-page"data-page="viewer"><divclass="app-topbar"><divclass="left app-button"data-backdata-autotitle></div><divclass="app-title">Viewer</div></div><divclass="app-content"></div></div>
// in your jsApp.controller('viewer',function(page, data){var photoViewer =newPhotoViewer(page, data.urls);});
// to use the viewerApp.load('viewer',{ urls:['http://i.imgur.com/yDK68Ff.jpg','http://i.imgur.com/rKIESYd.jpg','http://i.imgur.com/OTaodxO.jpg']});
http://www.peablog.com/project/appjs/