連軸轉的刷新,不斷變向的頁面轉換,以及tap事件的週期性的延遲僅僅是如今移動web環境使人頭疼事情的一小部分。開發者正試圖儘量的靠近原生應用,但卻常常被各類兼容問題,系統復位,和僵化的框架打亂步調。css
在這篇文章中,咱們將討論建立一個移動HTML 5 web app須要的最低限度的東西。主要觀點是去除如今移動框架試圖隱藏的隱含複雜性。你會看到一個簡約方法(使用核心的HTML 5APIs)和使你可以寫出本身的框架或給你如今在用的框架貢獻代碼的基本原則。html
一般狀況下,GPUs處理精細的3D建模或者CAD圖表,但這種狀況下,咱們想要原始的製圖(divs, 背景,下落式陰影的文字,圖像等等...) 能經過GPU平滑地展示出來而且有流暢的動畫。不幸的是,大多數前端開發者沒有考慮動畫處理的機制並將其裝載在第三方框架,可是這些核心的CSS3特性應 該被掩蓋嗎?讓我來給大家一些關於爲何關心這件事是十分重要的理由:前端
1. 內存分配和計算壓力- 若是你將全部元素都合成在DOM裏,僅僅是爲了硬件加速,在你的代碼基礎上繼續工做的另外一我的可能會想狠狠揍你一頓。html5
2. 電源消耗- 顯然地,當硬件開始工做,電源也隨之開始消耗。當進行移動端開發時,開發者開發移動應用必需要考慮設備多樣化的約束。廣泛流行的狀況是瀏覽器開發商開始使其產品能適應多樣的設備硬件。node
3. 衝突- 我曾經歷太小故障:將硬件加速應用到一部分可以加速的頁面。值得確信的是若是你有重複的加速區域是很是重要的。android
爲了儘量地使戶交互平滑而且接近真實,咱們必須使瀏覽器爲咱們工做。理想的狀況是,咱們想要移動設備的CPU創建初始化動畫,而後使GPU僅僅負責動畫處理過程當中合成不一樣的層。這就是translate3d, scale3d, translateZ作的事- 他們給了動畫元素到他們各自的層,所以容許設備能平滑渲染。若是想要了解更多加速合成,WebKit工做原理,Ariya Hidayat 在他的博客裏提供了許多信息。 css3
讓咱們看看開發移動WEB應用時最經常使用的三種用戶交互方法:滑動、翻轉、旋轉效果。web
你能夠在這個連接查看代碼的實際效果: http://slidfast.appspot.com/slide-flip-rotate.html (注意: 這個演示是爲移動設備創建的,因此請啓動模擬器,或者使用手機、平板電腦,或把你的瀏覽器窗口減少到約1024px或更小).ajax
首先,咱們將剖析滑動、翻轉、旋轉過渡,及如何使其加速。請注意每一個動畫是如何只需3、四行CSS和JavaScript便可實現的。正則表達式
在這三種經常使用效果中最經常使用的是滑動,滑動頁面變換模擬了移動應用的天然感受。滑動轉換用來向視圖區域帶來一個新的內容。
要實現滑動效果,首先咱們要聲明元素標籤:
1
2
3
4
5
6
7
8
9
10
11
|
<
div
id
=
"home-page"
class
=
"page"
>
<
h1
>Home Page</
h1
>
</
div
>
<
div
id
=
"products-page"
class
=
"page stage-right"
>
<
h1
>Products Page</
h1
>
</
div
>
<
div
id
=
"about-page"
class
=
"page stage-left"
>
<
h1
>About Page</
h1
>
</
div
>
|
注意咱們是如何讓頁面向左或向右演出的。本質上,它能夠是任何方向,但水平是最多見的。
咱們如今只需幾行CSS就能夠產生有硬件加速的動畫。咱們交換頁面上的div元素的class時,動畫就會實際發生。
1
2
3
4
5
6
7
|
.page {
position
:
absolute
;
width
:
100%
;
height
:
100%
;
/*activate the GPU for compositing each page */
-webkit-transform: translate
3
d(
0
,
0
,
0
);
}
|
translate3d(0,0,0)做爲「銀彈」方法而聞名。
當用戶點擊一個導航元素,咱們執行下面的JavaScript來交換class。沒有第三方框架被使用,這是純JavaScript!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
function
getElement(id) {
return
document.getElementById(id);
}
function
slideTo(id) {
//1.) the page we are bringing into focus dictates how
// the current page will exit. So let's see what classes
// our incoming page is using. We know it will have stage[right|left|etc...]
var
classes = getElement(id).className.split('
');
//2.) decide if the incoming page is assigned to right or left
// (-1 if no match)
var stageType = classes.indexOf('
stage-left
');
//3.) on initial page load focusPage is null, so we need
// to set the default page which we'
re currently seeing.
if
(FOCUS_PAGE ==
null
) {
// use home page
FOCUS_PAGE = getElement(
'home-page'
);
}
//4.) decide how this focused page should exit.
if
(stageType > 0) {
FOCUS_PAGE.className =
'page transition stage-right'
;
}
else
{
FOCUS_PAGE.className =
'page transition stage-left'
;
}
//5. refresh/set the global variable
FOCUS_PAGE = getElement(id);
//6. Bring in the new page.
FOCUS_PAGE.className =
'page transition stage-center'
;
}
|
stage-left或stage-right成爲stage-center,會推進頁面滑入視圖中心。咱們徹底依靠CSS3完成繁重的工做。
1
2
3
4
5
6
7
8
9
10
11
12
|
.stage-
left
{
left
:
-480px
;
}
.stage-
right
{
left
:
480px
;
}
.stage-
center
{
top
:
0
;
left
:
0
;
}
|
接下來,讓咱們看看處理移動設備檢測與適應的CSS。咱們能夠定位每種設備和每種分辨率(參考 媒體查詢解析)。 我在演示中使用的只是幾個簡單的例子來覆蓋移動設備上大多數的豎立和橫放視圖。這對應用每種設備自己的硬件加速功能也頗有用。好比,由於Webkit的桌 面版本加速了全部轉換元素(無論是二維仍是三維),因此在這個水平上創建媒體查詢和排除加速頗有意義。注意,在Android Froyo 2.2+如下,硬件加速技巧不會提供任何速度的改進。全部合成都是在軟件內部實現的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/* iOS/android phone landscape screen width*/
@media
screen
and (max-device-
width
:
480px
) and (orientation:
landscape
) {
.stage-
left
{
left
:
-480px
;
}
.stage-
right
{
left
:
480px
;
}
.page {
width
:
480px
;
}
}
|
在移動設備上,翻轉實際上以把頁面擊飛(譯者注:若是你熟悉棒球,很容易想像)而聞名。在這裏咱們用一些簡單的 JavaScript 在iOS 和 Android (基於WebKit)設備上來處理這個事件。
在這個地址可查看實際執行效果http://slidfast.appspot.com/slide-flip-rotate.html.
當處理觸摸事件和轉換效果時,你要作的第一件事就是得到元素當前位置的句柄。在WebKitCSSMatrix上能夠看到更多信息。
1
2
3
4
5
|
function
pageMove(event) {
// get position after transform
var
curTransform =
new
WebKitCSSMatrix(window.getComputedStyle(page).webkitTransform);
var
pagePosition = curTransform.m41;
}
|
因爲咱們爲頁面翻轉使用的是CSS3的ease-out轉換,usualelement.offsetleft不會工做。
下一步咱們要找出用戶翻轉的是哪一個方向,並對事件(頁面導航)設定一個發生的閾值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
if
(pagePosition >= 0) {
//moving current page to the right
//so means we're flipping backwards
if
((pagePosition > pageFlipThreshold) || (swipeTime < swipeThreshold)) {
//user wants to go backward
slideDirection = 'right
';
} else {
slideDirection = null;
}
} else {
//current page is sliding to the left
if ((swipeTime < swipeThreshold) || (pagePosition < pageFlipThreshold)) {
//user wants to go forward
slideDirection = '
left';
}
else
{
slideDirection =
null
;
}
}
|
你會注意到咱們測量擊打時間是毫秒級的。這容許導航事件在用戶快速點擊屏幕來翻頁時也會發生。
爲了定位頁面和當手指正觸摸屏幕時使動畫看起來天然,咱們在每次事件觸發後都使用CSS3轉換。
1
2
3
4
5
6
7
8
9
10
|
function
positionPage(end) {
page.style.webkitTransform =
'translate3d('
+ currentPos +
'px, 0, 0)'
;
if
(end) {
page.style.WebkitTransition =
'all .4s ease-out'
;
//page.style.WebkitTransition = 'all .4s cubic-bezier(0,.58,.58,1)'
}
else
{
page.style.WebkitTransition =
'all .2s ease-out'
;
}
page.style.WebkitUserSelect =
'none'
;
}
|
我想玩弄一下三次曲線來讓轉換帶有最好的天然感受,但ease-out已經玩了這個花樣。
最後,爲讓導航發生,咱們必須調用咱們以前在上一個演示裏定義的slideTo()方法。
1
2
3
4
5
6
7
8
|
track.ontouchend =
function
(event) {
pageMove(event);
if
(slideDirection ==
'left'
) {
slideTo(
'products-page'
);
}
else
if
(slideDirection ==
'right'
) {
slideTo(
'home-page'
);
}
}
|
接下來,讓咱們來看看在本演示使用的旋轉動畫。在任什麼時候候,你能夠旋轉頁面將看到180度旋轉後反面的「聯繫人」菜單選項。 一樣的,只須要幾行CSS和一些JavaScript指定一個點擊時的transition class。注:旋轉過渡則沒法正確的在大多數版本的 Android上呈現,由於它缺少3D CSS transform 的支持。不幸的是,Android提供了「側手翻」頁面旋轉特性,來替代翻轉。咱們建議在android獲得支持以前使用transition來進行翻轉。
正面與背面的基本結構:
1
2
3
4
5
6
7
8
|
<
div
id
=
"front"
class
=
"normal"
>
...
</
div
>
<
div
id
=
"back"
class
=
"flipped"
>
<
div
id
=
"contact-page"
class
=
"page"
>
<
h1
>Contact Page</
h1
>
</
div
>
</
div
>
|
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
function
flip(id) {
// get a handle on the flippable region
var
front = getElement(
'front'
);
var
back = getElement(
'back'
);
// again, just a simple way to see what the state is
var
classes = front.className.split(
' '
);
var
flipped = classes.indexOf(
'flipped'
);
if
(flipped >= 0) {
// already flipped, so return to original
front.className =
'normal'
;
back.className =
'flipped'
;
FLIPPED =
false
;
}
else
{
// do the flip
front.className =
'flipped'
;
back.className =
'normal'
;
FLIPPED =
true
;
}
}
|
CSS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/*----------------------------flip transition */
#bac
k,
#front {
position
:
absolute
;
width
:
100%
;
height
:
100%
;
-webkit-backface-
visibility
:
hidden
;
-webkit-transition-duration: .
5
s;
-webkit-transform-style: preserve
-3
d;
}
.
normal
{
-webkit-transform: rotateY(
0
deg);
}
.flipped {
-webkit-user-select: element;
-webkit-transform: rotateY(
180
deg);
}
|
如今咱們講完基本變換的方法了,讓咱們看看它們是如何工做和合成的。
爲了使這個奇妙的調試會話得以發生,讓咱們啓動你喜歡的一個IDE和瀏覽器。我使用Mac,所以操做可能和你的操做系統的命令與方式都不一樣。首先我在命令行設置一些調試中使用的環境變量,而後啓動Safari瀏覽器。打開Terminal,鍵入如下內容:
這樣就能開啓Safari的兩個調試助手功能。CA_COLOR_OPAQUE 會向咱們展示哪一個元素被實際合成和加速了。 CA_LOG_MEMORY_USAGE 會向咱們展示當向backing store發送咱們的繪製操做時使用了多少內存。這能夠確切告訴你你給移動設備施加了多少壓力,以及可能提示你你對GPU的使用會消耗目標設備多少電量。
如今讓咱們啓動Chrome,這樣咱們能夠很好地看到每秒多少幀(FPS)的信息:
注意:不要在全部頁選項中激活 GPU 合成。當瀏覽器檢測到你標籤中的合成項目,會只在左邊角落顯示FPS計數器,而這不是咱們在本案例中想要的。
若是你在威力加強版的Chrome中查看本講座效果頁面,你會在左上方看到紅色的 FPS 計數器。
這就是咱們怎樣知道硬件加速功能被開啓的方法。這也給了咱們一個關於動畫如何運行的和你是否有任何疏漏的想法(繼續運行本應中止的動畫)。
另外一種讓硬件加速變得實際可視化的方法是,若是你經過先設置我上面提到的環境變量來用Safari打開相同的頁面。每一個被加速的DOM元素都會有一個紅色色調。這告訴了咱們到底層合成了哪些元素。注意,白色的導航由於不能加速而沒有變紅。
Chrome在 about:flags中也有一個相似的設置 「Composited render layer borders」。
另外一個看到合成層的好方式,是開啓這個選項以後查看WebKit的落葉演示。
最 後,要真正瞭解咱們的應用程序的圖形硬件性能,讓咱們來看看內存是如何被消耗的。這裏咱們能夠看到,咱們正在把繪圖指令產生的1.38MB數據推動到 Mac OS上的CoreAnimation緩衝區。核心動畫緩衝區是被OpenGL ES和GPU共享的,來建立你最終在屏幕上看到的像素。
當咱們簡單地調整一下瀏覽器窗口尺寸或把窗口最大化,咱們會看到當即膨脹了。
這 給你一個想法,內存是如何被消耗在移動設備上,只有當你調整瀏覽器的正確尺寸。若是你在調試或測試iPhone環境,請從320像素調整到480像素。我 們如今明白了硬件加速究竟如何工做的,以及怎樣來調試。這是一種用閱讀數字來了解的方式,但也是真正看到GPU內存緩衝區可視化工做的方式,確實讓事情變 得透明瞭。
如今是時候把咱們的頁面和資源緩存提高到一個新水平了。就像jQuery Mobile及其相似框架所使用的方法,咱們要用併發AJAX調用來預取和緩存咱們的網頁。
讓咱們來指出一些移動網絡的核心問題和咱們爲何須要這麼作的緣由:
從滑動,翻轉,和旋轉演示 構建代碼,咱們開始先加上一些二級頁面並連接到它們。而後咱們將解析連接並飛速建立轉換。
如你所見,這裏咱們利用了語義標記。僅僅是到另外一個頁面的連接。子頁面像它的父頁面同樣遵循相同的節點/類結構。咱們能夠更進一步的給"page" 節點使用data-*屬性,等等……這裏是位於一個單獨的html文件中(/demo2/home-detail.html)的詳細頁(子頁面),它將被 加載,緩存並在app加載時爲頁面轉換預先創建。
<div id="home-page" class="page"> <h1>Home Page</h1> <a href="demo2/home-detail.html" class="fetch">Find out more about the home page!</a> </div>
如今讓咱們來看看JS。爲簡單起見,我沒對代碼添加助手或進行優化。咱們在這裏作的是遍歷一個指定的DOM節點的數 組,挖出要提取和緩存的連接。注意,對於本演示,fetchAndCache()方法在頁面加載時被調用。咱們在下一節中檢測網絡鏈接時會再次使用它,並 決定它什麼時候該被調用。
var fetchAndCache = function() { // iterate through all nodes in this DOM to find all mobile pages we care about var pages = document.getElementsByClassName('page'); for (var i = 0; i < pages.length; i++) { // find all links var pageLinks = pages[i].getElementsByTagName('a'); for (var j = 0; j < pageLinks.length; j++) { var link = pageLinks[j]; if (link.hasAttribute('href') && //'#' in the href tells us that this page is already loaded in the DOM - and // that it links to a mobile transition/page !(/[\#]/g).test(link.href) && //check for an explicit class name setting to fetch this link (link.className.indexOf('fetch') >= 0)) { //fetch each url concurrently var ai = new ajax(link,function(text,url){ //insert the new mobile page into the DOM insertPages(text,url); }); ai.doGet(); } } } };
咱們確保經過使用「 AJAX 」對象進行了適當的異步發送處理。在 Working Off the Grid with HTML5 Offline中調用的一個AJAX裏有對使用localStorage的一個更高級的解釋。在這個例子中,你會看到一個基本用法,用來緩存每一個請求,並當服務器未返回成功的(200)響應時提供以前所緩存的對象。
function processRequest () { if (req.readyState == 4) { if (req.status == 200) { if (supports_local_storage()) { localStorage[url] = req.responseText; } if (callback) callback(req.responseText,url); } else { // There is an error of some kind, use our cached copy (if available). if (!!localStorage[url]) { // We have some data cached, return that to the callback. callback(localStorage[url],url); return; } } } }
不幸的是,因爲本地存儲使用UTF-16字符編碼,每一個字節被看成2個字節存儲,將咱們的存儲限制從5MB降到 總共只有2.6MB。 在應用程序緩存範圍以外提取和緩存這些頁面/標記的整個緣由在下一節中透露。
經過最近在HTML5中iframe元素的 進展,咱們如今有了一個簡單而有效的方式來解析AJAX調用返回給咱們的響應文本。有不少3000行腳本解析器和去除腳本標籤的正則表達式之類的東西。但 爲什麼不讓瀏覽器代爲作它最擅長的?在這個例子中,咱們要把響應文本寫到一個暫時隱藏的iframe中。咱們使用HTML5的「沙箱」屬性,它禁用腳本並提 供了許多安全特徵…
從規範上來說: 當設置了 sandbox 屬性後, 在Iframe的內容上開啓了一組額外的限制。 它的值應該是一組無序的、空格分隔的 token, 而且是大小寫敏感的。 能夠設置的值分別是 allow-forms, allow-same-origin, allow-scripts, 和 allow-top-navigation. 當屬性設置了之後, 內容處理後,將被看成同源,forms 和 scripts 將被禁止,指向其餘瀏覽上下文的 link 將被禁止,插件也被禁用。 爲了防止危險的 HTML 內容形成破壞, 它應使用一個 text/html-sandboxed MIME 類型.
var insertPages = function(text, originalLink) { var frame = getFrame(); //write the ajax response text to the frame and let //the browser do the work frame.write(text); //now we have a DOM to work with var incomingPages = frame.getElementsByClassName('page'); var pageCount = incomingPages.length; for (var i = 0; i < pageCount; i++) { //the new page will always be at index 0 because //the last one just got popped off the stack with appendChild (below) var newPage = incomingPages[0]; //stage the new pages to the left by default newPage.className = 'page stage-left'; //find out where to insert var location = newPage.parentNode.id == 'back' ? 'back' : 'front'; try { // mobile safari will not allow nodes to be transferred from one DOM to another so // we must use adoptNode() document.getElementById(location).appendChild(document.adoptNode(newPage)); } catch(e) { // todo graceful degradation? } } };
Safari 正確的阻止了 Node 從一個 doc 到另外一個的隱式移動。若是一個新的子節點在不一樣的 doc 上建立,將拋出一個錯誤。 那麼這裏咱們使用adopt Node,一切都很好。
那麼爲何還要用Iframe,而不只僅用innerHTML?即使innerHtml現在已經是html5規範的一部分,將服務器的響應直接插入未 檢查過的區域的作法也是有危害的。寫做本文期間,我發現幾乎全部人都是使用的innerHTML。如Jquery在其核心中使用,僅在發生異常時有一個回 調函數來處理。而JQuery Mobile 也是這樣使用的。固然我沒有針對innerHTML的"隨機中止工做"的情況作過任何嚴格的測試,但查看比較各個平臺的對iframe和 innerHTML的不一樣做用效果將十分有趣,更想知道那種方式的性能會好些...在這兩種方式下其實我都已經聽到了很多抱怨了。
既然咱們有能力來緩存(預測緩存)咱們的web應用,咱們必須提供更好的網絡鏈接類型檢測功能使得咱們的應用更加智能。
這就是爲何移動應用的開發在 在線/離線模式和鏈接速度下變得十分敏感的緣由。進入The Network Information API網絡信息API. 每次我在演講這個功能點的時,臺下總有人會舉起收提問"那咱們使用它作什麼呢?".那麼確定有種方式來開發一個超級智能的移動應用的。
第一煩人場景是...在高速列車上從移動設備訪問一個Web站點,網絡的鏈接在各個不一樣的時刻和不一樣的地理環境下極可能失去,所以致使各類不一樣的傳 輸速度。 (如, HSPA 或 3G在一些城鎮地區能夠用, 但偏遠地區可能只支持速度很慢的2G技術). 下面的代碼解決了網絡鏈接問題中的大部分場景。
接下來的代碼演示的是:
window.addEventListener('load', function(e) { if (navigator.onLine) { // new page load processOnline(); } else { // the app is probably already cached and (maybe) bookmarked... processOffline(); } }, false); window.addEventListener("offline", function(e) { // we just lost our connection and entered offline mode, disable eternal link processOffline(e.type); }, false); window.addEventListener("online", function(e) { // just came back online, enable links processOnline(e.type); }, false);
在 上述事件 的監聽中 , 咱們 必須 告訴 咱們 的 代碼是否被 事件 或 實際 頁面 請求 刷新所 調用 。 主要 的 緣由 是 由於 在 聯機 和 脫機 模式 之間 切換 時 , 不會觸發(fired) 關於頁面正在 加載中的 事件 。
下一步 , 咱們 作 一個 簡單 的 檢查是否存在 匿名 在線 或 加載 事件 。此代碼須要禁用連接重置, 當 從 脫機 模式切換 爲 聯機狀態 。這個應用須要 更 複雜 的功能, 你 可能 須要作一些邏輯插入來執行 恢復抓取內容和爲間歇性鏈接而處理UX。
function processOnline(eventType) { setupApp(); checkAppCache(); // reset our once disabled offline links if (eventType) { for (var i = 0; i < disabledLinks.length; i++) { disabledLinks[i].onclick = null; } } }
processOffine()函數也是一樣的過程。假設你想讓你的app到離線模式而且試圖恢復以前場景全部的事務。下面的代碼找出全部的外部連接而且讓它們失效,永遠在咱們離線的應用中捕獲用戶,hoho
function processOffline() { setupApp(); // disable external links until we come back - setting the bounds of app disabledLinks = getUnconvertedLinks(document); // helper for onlcick below var onclickHelper = function(e) { return function(f) { alert('This app is currently offline and cannot access the hotness');return false; } }; for (var i = 0; i < disabledLinks.length; i++) { if (disabledLinks[i].onclick == null) { //alert user we're not online disabledLinks[i].onclick = onclickHelper(disabledLinks[i].href); } } }
好,這是多好的東東。如今咱們的app知道處於何種鏈接狀態,當它在線時,咱們也能夠檢查鏈接類型,而且相應的調整它。我曾經監聽典型的北美網絡供應商下載而且潛在地給每種鏈接的添加了註釋。
function setupApp(){ // create a custom object if navigator.connection isn't available var connection = navigator.connection || {'type':'0'}; if (connection.type == 2 || connection.type == 1) { //wifi/ethernet //Coffee Wifi latency: ~75ms-200ms //Home Wifi latency: ~25-35ms //Coffee Wifi DL speed: ~550kbps-650kbps //Home Wifi DL speed: ~1000kbps-2000kbps fetchAndCache(true); } else if (connection.type == 3) { //edge //ATT Edge latency: ~400-600ms //ATT Edge DL speed: ~2-10kbps fetchAndCache(false); } else if (connection.type == 2) { //3g //ATT 3G latency: ~400ms //Verizon 3G latency: ~150-250ms //ATT 3G DL speed: ~60-100kbps //Verizon 3G DL speed: ~20-70kbps fetchAndCache(false); } else { //unknown fetchAndCache(true); } }
fetchAndCache進程,有不少的設置參數,可是在此我僅僅讓他執行同步(給參數false)的或者異步(給參數true)的去取給定鏈接的資源。
Edge (同步) 請求時間線
WIFI (異步) 請求時間線
這 容許基於慢速或快速鏈接對用戶體驗的調整至少採起一些方法。這毫不是終結一切的解決方案。另外一個要作的是,在慢速鏈接上,當應用程序仍在後臺獲取某個連接 的頁面時,若是點擊這個連接,要拋出一個加載中模態。這個關鍵思想是減小延遲,同時用最新最棒的HTML5提供的對用戶的鏈接充分利用其所有能量。點此查看檢測網絡的演示.
移動HTML5應用程序剛剛上路。如今你能夠看見很是簡單且基礎的徹底圍繞 HTML5 建立的移動「框架」以及配套技術。我認爲對開發者來講,重要的是在覈心中處理解決這些問題,且不要用封裝掩蓋它。