1. 程式人生 > >移動Web單頁應用開發實踐——頁面結構化

移動Web單頁應用開發實踐——頁面結構化

http://www.maxzhang.com/

1. 前言

在開發面向現代智慧手機的移動Web應用的時候,無法避免一個事實,就是需要開發單頁應用(Single Page WebApp)。對於不同的系統需求,單頁應用的粒度會不同,可能是整個系統都使用一個頁面裝載,也可能是按模組分為獨立頁面裝載。在開發單頁應用時第一個要處理的問題就是頁面結構化,由於多個功能集中在一個頁面呈現,就必然需要考慮如何實現多個檢視佈局?如何實現檢視之間動畫切換?等問題。

下面我就來講述下手機搜狐前端團隊在單頁應用開發的頁面結構化上做過的一些嘗試與努力。

2. 頁面檢視

在講頁面結構化之前需要先理解檢視的概念,檢視是單頁應用開發中最常見的模組,通常在一個單頁應用中,會有多個檢視存在,每一個檢視都可以處理一部分業務功能,所有檢視的功能集就是單頁應用所能處理業務的最大能力。下面介紹幾種單頁應用中最常出現的幾種檢視。

2.1 單檢視層

三段式結構是單檢視的一種最基本佈局方式,如下圖:

structure-1

單檢視並不一定都有head或foot,所以Header、Footer使用虛線來表示。多數應用中還會有導航條(Navigatior),但一般情況下導航條會被計算為Header或Content的一部分,而不會獨立存在。

2.2 側邊欄

側邊欄是一種特殊的檢視,在不顯示時,當前檢視是蓋在側邊欄至上的,當它被撥出時,檢視一部分滑出螢幕外,側邊欄才被顯示出來,它的高度等於頁面可視區域的高度。

顯示前:

structure-2

顯示後:

structure-3

2.3 封面圖

封面圖與側邊欄類似,也是一個特殊的檢視。封面圖一般會在頁面初始時候出現,而後消失,消失之後就不再出現。它的檢視層級是最高的,並且完全覆蓋於其他頁面元素,它的高度會大於或等於可視區域的高度。

structure-4

3 多檢視佈局

單頁應用中第一個要思考的問題就是:如何實現多檢視的佈局?通常我們會將檢視的定位設定為position:absolute,這是一種簡單又實用的方法。在一個時間節點上,頁面可視區域只能有一個可見的當前檢視,虛線表示其他檢視,在頁面可視區域之外不可見(display:none),如下圖:

structure-5

使用偽程式碼表示:

<style type="text/css">
    .view {
        position: absolute;
        top: 0;
        left: 0;
        z-index: 99;
        display: none;
        width: 100%;
        height: 100%;
    }
    .current {
        z-index: 100;
        display: block;
    }
</style>
<div class="view current"></div>
<div class="view"></div>

此時,我們需要思考另一個問題:如何實現當前檢視的Content區域內容滾動?檢視的樣式高度設定為height:100%,將檢視高度設定為一屏高的目的是為了方便實現檢視動畫切換的效果(檢視動畫切換會在後面詳細的講)。但這樣做會導致另一個問題,高度為一屏高意味著瀏覽器滾動條失效,無法使用瀏覽器滾動條滾動頁面。

3.1 基於iScroll的多檢視佈局

現在比較流行的一種解決方案是使用iScroll元件實現固定區域滾動,這樣就能解決Content區域的滾動問題,在手機搜狐的早期專案也是這麼做的。此外,使用iScroll還額外帶來了一些好處,如:

  • Header區域能固定在頁面頂部,不會因為Content區域滾動導致Header被頂上去;
  • 單檢視的高度控制在一屏高,這樣有利於實現檢視之間的動畫切換;

對於這種結構的應用,在使用檢視切換的時候就非常好做,使用CSS3的transition來完成動畫切換,如下圖:

structure-6

使用偽程式碼表示:

<style type="text/css">
    .current.out {
        -webkit-transition: -webkit-transform 400ms;
        -webkit-transform: translate3d(-100%,0,0);
    }
    .next {
        display: block;
        -webkit-transform: translate3d(100%,0,0);
    }
    .next.in{
        -webkit-transition: -webkit-transform 400ms;
        -webkit-transform: translate3d(0,0,0);
    }
</style>
<div class="view current out"></div>
<div class="view next in"></div>

檢視切換的動畫效果可以根據業務需求定製,比如:由左向右滑動、由右向左、由上到下、右下到上等都是可以的。在完成切換動畫時,再將next檢視的狀態設定為current,如下:

<div class="view"></div>
<div class="view current"></div>

下圖是專案中使用的一個由下往上動畫切換效果:

structure-7

3.2 iScroll頁面結構下的側邊欄

使用iScroll的頁面結構,無論是側邊欄還是封面圖都非常好實現,看虛擬碼:

側邊欄,預設狀態

<style type="text/css">
    .sidebar {
        z-index: 50;
        display: block;
        width: 80%;
    }
    .sidebar.show + .current {
        -webkit-transition: -webkit-transform 400ms;
        -webkit-transform: translate3d(80%,0,0);
    }
    .sidebar.hide + .current {
        -webkit-transition: -webkit-transform 400ms;
        -webkit-transform: translate3d(0,0,0);
    }
</style>
<div class="view sidebar"></div>
<div class="view current"></div>

側邊欄顯示時

<div class="view sidebar show"></div>
<div class="view current"></div>

側邊欄隱藏時,當hide動畫結束之後,移除hide樣式

<div class="view sidebar hide"></div>
<div class="view current"></div>

3.3 iScroll頁面結構下的封面圖

封面圖的實現與側邊欄差不多。

封面圖,預設狀態

<style type="text/css">
    .cover {
        z-index: 200;
        display: block;
        visibility: hidden;
        opacity: 0;
    }
    .cover.show {
        visibility: visible;
        -webkit-transition: opacity 400ms;
        opacity: 1;
    }
    .cover.hide {
        visibility: visible;
        -webkit-transition: opacity 400ms;
        opacity: 0;
    }
</style>
<div class="view cover"></div>
<div class="view current"></div>

封面圖顯示時

<div class="view cover show"></div>
<div class="view current"></div>

封面圖隱藏時,當hide動畫結束之後,移除hide樣式

<div class="view cover hide"></div>
<div class="view current"></div>

在專案中的實現效果:

structure-8

3.4 iScroll對內容重新整理的支援

structure-9

4. 多檢視佈局,新的探索

對於單頁應用來說,iScroll確實是一個非常優秀的解決方案,但是iScroll缺有一個最大的缺陷——慢,滾動的效能與瀏覽器原生實現相比,在低端的移動裝置上有明顯示卡頓,這點我在另一片博文中也提到過《移動Web產品前端開發口訣——“快”》。

Note:目前有一個新的趨勢,瀏覽器經過一兩年的發展,Android下已經優化的相當不錯,iScroll在一些較低端的移動裝置上,效能表現得比以前要好非常多,比如小米1,早期的米1還在執行UC7.x的版本時,iScroll明顯的卡,現在在UC9.x下,iScroll也能執行得比較流暢了。

4.1 Fixed+原生Scroll

在此之下,我們也做了一些新的嘗試,第一嘗試就是放棄使用iScroll元件。放棄之後遇到的第一個問題,如何使Header固定位置在頂部?由此,我們使用了原生的CSS特性position:fixed,如下圖:

structure-10

Fixed在一些移動裝置瀏覽器上有相容問題,我找到了一種能檢測瀏覽器是否支援position:fixed的方法,這個也發一篇博文《移動Web開發,4行程式碼檢測瀏覽器是否支援position:fixed》,在檢測到瀏覽器不支援fixed時,可以使用absolute作為替代方案,監聽window的scroll事件,每次scroll動作結束時,重新計算一次Header的top值,將其定位到頁面頂部。

另外強調一點,不要在Fixed區域中直接使用input或textarea元素。在fixed元素中的input獲取焦點之後,彈出軟鍵盤會帶來很多額外的問題,如:

  • 在iOS下軟鍵盤彈出,fixed定位會出問題;
  • 在Android下軟體盤彈出,可能會導致輸入區域被遮擋;

點選input彈出一個新檢視來完成後續輸入,是一種比較好的解決方案,下圖是一個基於iScroll的頁面結構實現:

structure-11

4.2 原生Scroll下的檢視切換

使用了原生Scroll之後,帶來最大的改變是檢視切換動畫的變化。使用iScroll的頁面結構,檢視的高度固定,並且是position:absolute定位,所以非常容易做檢視切換。

換成原生Scroll之後,想使用一個比較緩和的動畫過渡效果是非常困難的,可選的動畫效果十分有限,經過了很多試驗之後,最後選擇使用淡入-淡出的動畫效果,這是一種折中的方法。最初在完成這種動畫實現的時候,編碼的方法比較簡單,就是將當前檢視淡出,下一檢視淡入,如下圖:

structure-12

後來在做了更多嘗試之後,開發出了一種相容更強的淡入-淡出動畫過渡。技術要點就是使用一個幕布層(mask)實現淡入效果,在mask完成淡入之後,再完成實際的檢視的切換,操作步驟大致如下:

  • 建立一個幕布層<div class="mask"></div>,mask為position:absolute定位,初始為透明狀態,背景設定為白色或其他顏色,並使mask蓋在當前檢視上面;
  • mask使用transition實現opacity:1的動畫過渡,當完成動畫時,mask將會把當前檢視完全遮住;
  • 最後,直接將當前檢視隱藏,將下一檢視顯示既可;
  • 完成所有動作之後,隱藏mask;

效果圖:

structure-13

4.3 原生Scroll頁面結構下的側邊欄

側邊欄的結構也變得複雜了一些,使用原生Scroll之後,body的高度會被內容區域撐到很高,但側邊欄還是必須保證一屏高。所以我在側邊欄顯示時,將html與body的高度控制為一屏高,這樣可以防止頁面被滾動。使用偽程式碼表示:

側邊欄,預設狀態

<html class="frame">
<head>
<style type="text/css">
    .frame {
        height: 100%;
    }
    .sidebar {
        background-color: red;
        position: absolute;
        z-index: 50;
        width: 80%;
        height: 100%;
    }
    .scroller {
        background-color: green;
        position: relative;
        z-index: 100;
        height: 2000px;
    }
    .sidebar-show body, .sidebar-hide body {
        height: 100%;
    }
    .sidebar-show .scroller {
        overflow: hidden;
        height: 100%;
        -webkit-transition: -webkit-transform 400ms;
        -webkit-transform: translate3d(80%,0,0);
    }
    .sidebar-hide .scroller {
        overflow: hidden;
        height: 100%;
        -webkit-transition: -webkit-transform 400ms;
        -webkit-transform: translate3d(0,0,0);
    }
</style>
</head>
<body>
<div class="sidebar"></div>
<div class="scroller"></div>
</body>
</html>

側邊欄顯示時,在html元素上增加一個樣式sidebar-show

<html class="frame sidebar-show">

側邊欄隱藏時,將html元素上的樣式替換成sidebar-hide,當hide動畫結束之後,移除hide樣式

<html class="frame sidebar-hide">

在專案中的實際效果:

structure-14

另外,將側邊欄設定為position:fixed定位會是另一種實現思路。

4.4 原生Scroll頁面結構下的封面圖

封面圖的實現與側邊欄差不多,使用偽程式碼表示:

封面圖,預設為顯示狀態

<html class="frame cover-show">
<head>
<style type="text/css">
    .frame, .frame body {
        height: 100%;
    }
    .cover {
        background-color: red;
        position: absolute;
        z-index: 200;
        width: 100%;
        height: 100%;
    }
    .scroller {
        background-color: green;
        position: relative;
        z-index: 100;
        height: 2000px;
    }
    .cover-show body, .cover-hide body {
        height: 100%;
    }
    .cover-show .scroller {
        overflow: hidden;
        height: 100%;
    }
    .cover-hide .cover {
        -webkit-transition: opacity 400ms;
        opacity: 0;
    }
</style>
</head>
<body>
<div class="cover"></div>
<div class="scroller"></div>
</body>
</html>

封面圖隱藏時,將html元素上的樣式替換成cover-hide,當hide動畫結束之後,移除hide樣式

<html class="frame cover-hide">

專案中的應用:

structure-15

4.5 原生Scroll頁面結構下,內容重新整理的實現

一般情況下,我們會頁面底部放一個載入更多的按鈕,讓使用者點選按鈕載入下一頁內容,如下圖:

structure-16

又或者,監聽window的scroll事件,當頁面發生滾動時,監測是否滾動到頁面底部,自動載入下一頁內容。這兩種方式都能很好的解決載入下一頁的業務需求,但是對於載入最新或重新整理的操作只能在頁面中放置一個重新整理按鈕來完成業務需求。

對於Pull Up/Down Request的操作,在原生Scroll下,幾乎是無法實現的。但我依然希望能找到一種方法,實現Pull Request操作。

現在我正在研究一種模擬Pull操作的解決方案,已經有了一個雛形,並實現了一些功能。下面這個示例中沒有使用任何的iScroll技術,完全使用原生Scroll實現頁面滾動,並且滾動到頁面底部後可以完成Pull Up操作,如下圖:

structure-17

這個技術的實現原理並不複雜,就是在頁面滾動到底部時,建立一個空白層,模擬Pull Up手勢拖動頁面的效果。

structure-18

我後面會封裝成一個元件放在GitHub上分享給大家。

5 結束語

手機搜狐目前還是一個年輕的前端團隊,在手機搜狐的一年半時間,積累和很多有關移動端Web開發的經驗,寫這篇文章希望能將自己在移動Web方面的一些經驗分享給大家,同時,也希望能有更多的移動Web開發者能互相交流。

相關推薦

移動Web應用開發實踐——頁面結構

http://www.maxzhang.com/ 1. 前言 在開發面向現代智慧手機的移動Web應用的時候,無法避免一個事實,就是需要開發單頁應用(Single Page WebApp)。對於不同的系統需求,單頁應用的粒度會不同,可能是整個系統都使用一個頁面裝載,也

AngularJS仿拉勾網WebApp開發移動應用實戰 AngularJS實戰 共7章

第1章 課程介紹    講解課程開發內容和開發流程。前端主流開發框架與AngularJS對比。介紹AngularJS的基本概念。第2章 基礎知識介紹介紹單頁應用以及AngularJS web app的構成。講解css預編譯語言less的使用。第3章 使用構建工具講解構建工具gul

Vue 應用 動態修改頁面 title

使用 Vue + VueRouter 開發單頁應用時,有些需要修改頁面 title 的場景,可以通過在 router 中新增 meta 元資訊來實現: routes: [{ name: 'home', path: '/home/:openname',

做一個gulp+webpack+vue的應用開發架子

最近專案上的事情不多,根據我自己的開發習慣,決定開發一些簡單的開發架子,方便以後事情多的時候直接套用。本文講的一個gulp+webpack+vue的單頁應用架子,想要達到的目的:可以通過命令打包開發chunk,並支援熱替換可以通過命令打包可釋出的chunk支援路由路由中的程式碼實現按需載入用CommonJs的

基於Redux架構的應用開發總結(一)

寫在前面 “大學四年,細細回味。大一,面帶稚嫩的面龐,一腔傻傻的熱情。可愛帥氣的小涵妹,帶我認識時尚,好基友終生難忘。大二,踏上程式設計師之旅,曦點無緣,Smart不棄,恩師點撥學長提攜,滴水之恩湧泉報。大三,有了自己的團隊,樂雁老朱,程式設計遊戲我們都

《Flask Web開發——基於Python的Web應用開發實踐》一字一句上機實踐(下)

屬性 一個用戶 臨時 target 說明 實戰 分享圖片 ace 庫文件 目錄 前言 第8章 用戶認證 第9章 用戶角色 第10章 用戶資料 第11章 博客文章 第12章 關註者 第13章 用戶評論 第14章 應用編程接口 前言

vue 應用點擊某個鏈接,跳轉到新頁面的方式

subst ams out 詳情 targe god str blank attribute <router-link class="goDetail" :to="{name: ‘detail‘,params: {id:item.id}}" target = _bl

vue應用中,使用setInterval()定時向伺服器獲取資料,後來跳轉頁面後,發現還在不停的獲取資料。

使用VUE開發單頁專案時遇到這樣的問題,mounted中使用setInterval()定時向伺服器獲取資料,後來跳轉頁面後,發現還在不停的獲取資料。我以為是因為我路由用的push導致的,改成replace也是這樣,後來就在部落格園找到答案了,如下: http://www.cnblogs.com/zzbp/p/

Web應用 9 應用釋出準備

   讓Googel找到他們    由於單頁應用的所有HTML都由JavaScript生成,所以需要使用不同的方法。而爬蟲不會執行JS    提供詳細的日誌資訊:網站流量、使用者行為和錯誤資訊。而單頁應用把使用者互動邏輯移到了客戶端,所以需要使用另外的方法。    提升響應

應用(SPA)前端javascript如何阻止按下返回鍵頁面回退

這幾年單頁應用很流行,利用html5新特性history.pushState/replaceState 可以操作URL無重新整理動態載入html內容,達到比較好的使用者體驗。 不過經常會遇到這樣一個問題,那就是使用者總是習慣於按返回鍵來返回上一頁,移動端頁面有彈框的時候,使

實踐】用路由為webApp應用提供多入口

前言 我1個月前開發的webApp是個單入口的單頁應用,那個時候需求沒有很複雜,僅僅是一個購物的站點,但後來不停地新增功能,而且近期還提出需要把webAPP的部分頁面分享到 IOS webView、微信朋友圈,並且能完成完整的業務邏輯。 我的策略

2018-2019-2 20189203 移動平臺應用開發實踐第四周學習總結

nta 測試 區別 構造函數 類型參數 一句話 操作 get lee 第12、13、14、15章學習 第12章 枚舉 一、常量 在JDK1.5 之前,我們定義常量都是: public static final.... 。現在好了,有了枚舉,可以把相關的常量分組到一個枚舉類

2018-2019-2 20189203 移動平臺應用開發實踐第六周學習總結

方便 不同 技術 rest 接收 ttext 生命周期 androi linear 第23、24、25、26章學習 第23章 Android簡介 Android是一種多用戶的Linux系統。它快速達到巔峰的原因之一是,它使用Java作為編程語言。所有Android類和接口

2018-2019-2 20189203 移動平臺應用開發實踐第八周學習總結

ets pad ons theme ... string rgb 聯系 deb 第31、32、33、34章 第31章 ListView (一)、ListView 概念: ListView是Android中最重要的組件之一,幾乎每個Android應用中都會使用ListVie

2018-2019-2 20189203 移動平臺應用開發實踐第十周學習總結

ace The ont ces horizon 產生 xtend 成了 例如 第39,40,41,42章 第39章 偏好 在Android應用中,我們常需要記錄用戶設置的一些偏好參數,,此時我們就需要用SharedPreferences和Editor將這些信息保存下來,在

手把手教你把web應用丟到伺服器上(應用+ 服務端渲染)

> 前兩篇文章中,我分別介紹了框架的搭建[利用vue-cli + vant搭建一個移動端開發模板](https://juejin.im/post/5eb4cfc2e51d453914497c55),並且把專案中axios請求和vuex的用法做了簡要的介紹[如何在專案裡管理好axios請求與vuex](h

vue-router應用簡單示例(一)

問題 clas 做了 設置 new scope 文件的 log target 請先完成了項目初始化,具體請看我另一篇博文。vue項目初始化 看一下完成的效果圖,很典型的單頁應用。 .vue後綴名的單文件組件 這裏先說一下我對組件的理解。組件,顧名思義就是一組元素組成的

vue-router應用簡單示例(二)

數據 prop tps div -1 可重用性 example 定位 .com 我們先來理一下思路。 圖1:main.js 引入vue,App.vue,router/index.js文件 聲明要渲染的Id為app,將App.vue中的模版渲染到入口界面(就

應用的三大優勢及監控方法

地理 多個 sof 利用 ref .text 網絡通信 tex mat 近期。開發人員們越來越理解。為用戶提供愉悅的用戶體驗的重要性。這也是實現業務目標的關鍵因素。作為高端用戶,開發人員本身也越來越意識到站點的性能對提升用戶體驗的重要性。 相同地,開發