通過預加載解決切頁白屏問題,通過封裝原生動畫解決SPA模式的動畫卡頓
[TOC]
## 頁面初始化
在app開發中,若要使用HTML5+擴展api,必須等plusready事件發生后才能正常使用,mui將該事件封裝成了mui.plusReady()方法,涉及到HTML5+的api,建議都寫在mui.plusReady方法中。如下為打印當前頁面URL的示例:
```
mui.plusReady(function(){
console.log("當前頁面URL:"+plus.webview.currentWebview().getURL());
});
```
> mui.init() mui插件初始化
> mui.ready() 當DOM準備就緒時,指定一個函數來執行。
> 代碼塊激活字符:????minit
## 創建子頁面
在mobile app開發過程中,經常會出現共用的導航欄或者選項卡,每次打開頁面都需要重新渲染,而且容易出現卡頭卡尾的現象。并且此時若使用局部滾動,在android手機上會出現滾動不流暢的問題;
mui現在提供兩種解決方案:
第一種(官方推薦):在plus環境下,使用原生titleNView以及原生tabbar來替換頁面的導航欄或者選項卡。在頁面打開時,渲染已經完成,讓你的應用更接近原生app。具體做法:原生titleNView參考`mui.openWindow`的[示例3](https://dev.dcloud.net.cn/mui/window/#titleNView),原生tabbar示例參考[ask教程示例](http://ask.dcloud.net.cn/article/12602)
第二種:通過雙webview模式解決,此種情況適用于需要上下拉刷新的列表頁面。將需要滾動的區域通過單獨的webview實現,完全使用原生滾動。具體做法則是:將目標頁面分解為主頁面和內容頁面,主頁面顯示卡頭卡尾區域,比如頂部導航、底部選項卡等;內容頁面顯示具體需要滾動的內容,然后在主頁面中調用mui.init方法初始化內容頁面。
```
mui.init({
subpages:[{
url:your-subpage-url,//子頁面HTML地址,支持本地地址和網絡地址
id:your-subpage-id,//子頁面標志
styles:{
top:subpage-top-position,//子頁面頂部位置
bottom:subpage-bottom-position,//子頁面底部位置
width:subpage-width,//子頁面寬度,默認為100%
height:subpage-height,//子頁面高度,默認為100%
......
},
extras:{}//額外擴展參數
}]
});
```
**參數說明:** styles表示窗口屬性,參考[5+規范中的WebviewStyle](https://www.dcloud.io/docs/api/zh_cn/webview.shtml#plus.webview.WebviewStyle);特別注意,height和width兩個屬性,即使不設置,也默認按100%計算;因此若設置了top值為非"0px"的情況,建議同時設置bottom值,否則5+ runtime根據高度100%計算,可能會造成頁面真實底部位置超出屏幕范圍的情況;left、right同理。
**示例:** Hello mui的首頁其實就是index.html加list.html合并而成的,如下:

index.html的作用就是顯示固定導航,list.html顯示具體列表內容,列表項的滾動是在list.html所在webview中使用原生滾動,既保證了滾動條不會穿透頂部導航,符合app的體驗,也保證了列表流暢滾動,解決了區域滾動卡頓的問題。 list.html就是index.html的子頁面,創建代碼比較簡單,如下:
```
mui.init({
subpages:[{
url:'list.html',
id:'list.html',
styles:{
top:'45px',//mui標題欄默認高度為45px;
bottom:'0px'//默認為0px,可不定義;
}
}]
});
```
> 代碼塊激活字符:????misubpage
## 打開新頁面
做web app,一個無法避開的問題就是轉場動畫;web是基于鏈接構建的,從一個頁面點擊鏈接跳轉到另一個頁面,如果通過有刷新的打開方式,用戶要面對一個空白的頁面等待;如果通過無刷新的方式,用Javascript移入DOM節點(常見的SPA解決方案),會碰到很高的性能挑戰:DOM節點繁多,頁面太大,轉場動畫不流暢甚至導致瀏覽器崩潰; mui的解決思路是:單webview只承載單個頁面的dom,減少dom層級及頁面大小;頁面切換使用原生動畫,將最耗性能的部分交給原生實現.

```
mui.openWindow({
url:new-page-url,
id:new-page-id,
styles:{
top:newpage-top-position,//新頁面頂部位置
bottom:newage-bottom-position,//新頁面底部位置
width:newpage-width,//新頁面寬度,默認為100%
height:newpage-height,//新頁面高度,默認為100%
......
},
extras:{
.....//自定義擴展參數,可以用來處理頁面間傳值
},
createNew:false,//是否重復創建同樣id的webview,默認為false:不重復創建,直接顯示
show:{
autoShow:true,//頁面loaded事件發生后自動顯示,默認為true
aniShow:animationType,//頁面顯示動畫,默認為”slide-in-right“;
duration:animationTime//頁面動畫持續時間,Android平臺默認100毫秒,iOS平臺默認200毫秒;
},
waiting:{
autoShow:true,//自動顯示等待框,默認為true
title:'正在加載...',//等待對話框上顯示的提示內容
options:{
width:waiting-dialog-widht,//等待框背景區域寬度,默認根據內容自動計算合適寬度
height:waiting-dialog-height,//等待框背景區域高度,默認根據內容自動計算合適高度
......
}
}
})
```
**參數:**
* `styles`
窗口參數,參考[5+規范中的WebviewStyle](https://www.dcloud.io/docs/api/zh_cn/webview.shtml#plus.webview.WebviewStyle);特別注意,height和width兩個屬性,即使不設置,也默認按100%計算;因此若設置了top值為非"0px"的情況,建議同時設置bottom值,否則5+ runtime根據高度100%計算,可能會造成頁面真實底部位置超出屏幕范圍的情況;left、right同理。
* `extras`
新窗口的額外擴展參數,可用來處理頁面間傳值;例如:
```
var webview = mui.openWindow({
url:'info.html',
extras:{
name:'mui' //擴展參數
}
});
console.log(webview.name);//輸出mui字符串
```
**注意:** 擴展參數僅在打開新窗口時有效,若目標窗口為預加載頁面,則通過mui.openWindow方法打開時傳遞的extras參數無效。
* `createNew`
是否重復創建相同id的webview;
為優化性能、避免app中重復創建webview,mui v1.7.0開始增加createNew參數,默認為false;判斷邏輯如下:
* createNew參數為為true,則不判斷重復,每次都新建webview;
* createNew參數為為fasle,則先查找當前App中是否已存在同樣id的webview,若存在則直接顯示;否則新創建并根據show參數執行顯示邏輯;
**注意**:plusReady事件僅在webview首次創建時觸發,使用`mui.openWindow`方法多次打開已存在的同樣id的webview時,是不會重復觸發plusReady事件的; 因此若業務寫在plusReady事件中,可能會出現執行結果和預期不一致的情況;此時可通過[自定義事件](https://dev.dcloud.net.cn/mui/event/#customevent)觸發; 案例參考:[mui.plusReady有時會失效](https://ask.dcloud.net.cn/question/6514);
* `show`
窗口顯示控制參數,具體參數如下:
* autoShow:目標窗口loaded事件發生后,是否自動顯示,默認為true;若為false,則僅創建但不顯示webview;若目標頁面為預加載頁面,則該參數無效;
* aniShow表示頁面顯示動畫,比如從右側劃入、從下側劃入等,具體可參考[5+規范中的AnimationTypeShow](https://www.dcloud.io/docs/api/zh_cn/webview.shtml#plus.webview.AnimationTypeShow)
* duration:顯示Webview窗口動畫的持續時間,單位為ms
* `waiting`
系統等待框參數
mui框架在打開新頁面時等待框的處理邏輯為:
> 顯示等待框-->創建目標頁面webview-->目標頁面loaded事件發生-->關閉等待框;
因此,只有當新頁面為新創建頁面(webview)時,會顯示等待框,否則若為預加載好的頁面,則直接顯示目標頁面,不會顯示等待框。waiting中的具體參數:
* autoShow:是否自動顯示等待框,默認為true;若為false,則不顯示等待框;注意:若waiting框的autoShow為true,但目標頁面不自動顯示,則需在目標頁面中通過如下代碼關閉等待框:`plus.nativeUI.closeWaiting();`
* title:等待框上的提示文字
* options表示等待框顯示參數,比如寬高、背景色、提示文字顏色等,具體可參考[5+規范中的WaitingOption](https://www.dcloud.io/docs/api/zh_cn/nativeUI.shtml#plus.nativeUI.WaitingOption)
**示例1:**Hello mui中,點擊首頁右上角的圖標,會打開關于頁面,實現代碼如下:
```
//tap為mui封裝的單擊事件,可參考手勢事件章節
document.getElementById('info').addEventListener('tap', function() {
//打開關于頁面
mui.openWindow({
url: 'examples/info.html',
id:'info'
});
});
```
因沒有傳入styles參數,故默認全屏顯示;也沒有傳入show參數,故使用slide-in-right動畫,新頁面從右側滑入。
**示例2:** 從A頁面打開B頁面,B頁面為一個需要從服務端加載的列表頁面,若在B頁面loaded事件發生時就將其顯示出來,因服務器數據尚未加載完畢,列表頁面為空,用戶體驗不好;可通過如下方式改善用戶體驗(最好的用戶體驗應該是通過[預加載](https://dev.dcloud.net.cn/mui/window/#preload)的方式):
> 第一步,B頁面loaded事件發生后,不自動顯示;
```
//A頁面中打開B頁面,設置show的autoShow為false,則B頁面在其loaded事件發生后,不會自動顯示;
mui.openWindow({
url: 'B.html',
show:{
autoShow:false
}
});
```
> 第二步,在B頁面獲取列表數據后,再關閉等待框、顯示B頁面
```
//B頁面onload從服務器獲取列表數據;
window.onload = function(){
//從服務器獲取數據
....
//業務數據獲取完畢,并已插入當前頁面DOM;
//注意:若為ajax請求,則需將如下代碼放在處理完ajax響應數據之后;
mui.plusReady(function(){
//關閉等待框
plus.nativeUI.closeWaiting();
//顯示當前頁面
mui.currentWebview.show();
});
}
```
> 代碼塊激活字符:????mopenwindow
## [打開帶原生導航欄的新頁面](https://dev.dcloud.net.cn/mui/window/#openWindowWithTitle)
使用父子 webview 或者同屏顯示多個 webview 的性能和資源消耗較大。非必要不推薦使用同屏多 webview 的方案,推薦使用原生導航欄方案代替。可以加快窗體進入速度,內存占用更少。
通過在 mui.openWindow 的 styles 節點中設置 titleNView 節點的相關參數,可實現繪制原生導航欄控件,具體可參考 5+ webviewStyles 中的 titleNView 節點中的[WebviewTitleNViewStyles](http://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewTitleNViewStyles)的詳細文檔。示例如下:
```
mui.openWindow({
url: webviewUrl,
id: webviewId,
styles: { // 窗口參數 參考5+規范中的WebviewStyle,也就是說WebviewStyle下的參數都可以在此設置
titleNView: { // 窗口的標題欄控件
titleText:"標題欄", // 標題欄文字,當不設置此屬性時,默認加載當前頁面的標題,并自動更新頁面的標題
titleColor:"#000000", // 字體顏色,顏色值格式為"#RRGGBB",默認值為"#000000"
titleSize:"17px", // 字體大小,默認17px
backgroundColor:"#F7F7F7", // 控件背景顏色,顏色值格式為"#RRGGBB",默認值為"#F7F7F7"
progress:{ // 標題欄控件的進度條樣式
color:"#00FF00", // 進度條顏色,默認值為"#00FF00"
height:"2px" // 進度條高度,默認值為"2px"
},
splitLine:{ // 標題欄控件的底部分割線,類似borderBottom
color:"#CCCCCC", // 分割線顏色,默認值為"#CCCCCC"
height:"1px" // 分割線高度,默認值為"2px"
}
}
}
});
```
**說明:** 老版本的`mui.openWindowWithTitle`已經廢除。
## 關閉頁面
三種方法
mui框架將窗口關閉功能封裝在`mui.back`方法中,具體執行邏輯是:
* 若當前webview為預加載頁面,則hide當前webview;
* 否則,close當前webview;
在mui框架中,有三種操作會觸發頁面關閉(執行mui.back方法):
* 點擊包含`.mui-action-back`類的控件
* 在屏幕內,向右快速滑動
* Android手機按下back按鍵
#### iOS平臺原生支持從屏幕邊緣右滑關閉
iOS平臺可通過popGesture參數實現從屏幕邊緣右滑關閉webview,參考[5+規范](https://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewStyle),若想禁用該功能,可通過setStyle方法設置popGesture為none。
hbuilder中敲`mheader`生成的代碼塊,會自動生成帶有返回導航箭頭的標題欄,點擊返回箭頭可關閉當前頁面,原因就是因為該返回箭頭包含`.mui-action-back`類,代碼如下:
```
<header class="mui-bar mui-bar-nav">
<a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
<h1 class="mui-title">標題</h1>
</header>
```
若希望在頂部導航欄之外的其它區域添加關閉頁面的控件,只需要在對應控件上添加`.mui-action-back`類即可,如下為一個關閉按鈕示例:
```
<button type="button" class='mui-btn mui-btn-danger mui-action-back'>關閉</button>
```
mui框架封裝的頁面右滑關閉功能,默認未啟用,若要使用右滑關閉功能,需要在`mui.init();`方法中設置swipeBack參數,如下:
```
mui.init({
swipeBack:true //啟用右滑關閉功能
});
```
mui框架默認會監聽Android手機的back按鍵,然后執行頁面關閉邏輯; 若不希望mui自動處理back按鍵,可通過如下方式關閉mui的back按鍵監聽;
```
mui.init({
keyEventBind: {
backbutton: false //關閉back按鍵監聽
}
});
```
除了如上三種操作外,也可以直接調用`mui.back()`方法,執行窗口關閉邏輯;
`mui.back()`僅處理窗口邏輯,若希望在窗口關閉之前再處理一些其它業務邏輯,則可將業務邏輯抽象成一個具體函數,然后注冊為mui.init方法的beforeback參數;beforeback的執行邏輯為:
* 執行beforeback參數對應的函數若返回false,則不再執行`mui.back()`方法;
* 否則(返回true或無返回值),繼續執行`mui.back()`方法;
示例:從列表打開詳情頁面,從詳情頁面再返回后希望刷新列表界面,此時可注冊beforeback參數,然后通過[自定義事件](https://dev.dcloud.net.cn/mui/event/#customevent)通知列表頁面刷新數據,示例代碼如下:
```
mui.init({
beforeback: function(){
//獲得列表界面的webview
var list = plus.webview.getWebviewById('list');
//觸發列表界面的自定義事件(refresh),從而進行數據刷新
mui.fire(list,'refresh');
//返回true,繼續頁面關閉邏輯
return true;
}
});
```
注意:beforeback的執行返回必須是同步的(阻塞模式),若使用nativeUI這種異步js(非阻塞模式),則可能會出現意想不到的結果;比如:通過`plus.nativeUI.confirm()`彈出確認框,可能用戶尚未選擇,頁面已經返回了(beforeback同步執行完畢,無返回值,繼續執行`mui.back()`方法,nativeUI不會阻塞js進程):在這種情況下,若要自定義業務邏輯,就需要復寫`mui.back`方法了;如下為一個自定義示例,每次都需要用戶確認后,才會關閉當前頁面
```
//備份mui.back,mui.back已將窗口關閉邏輯封裝的比較完善(預加載及父子窗口),因此最好復用mui.back
var old_back = mui.back;
mui.back = function(){
var btn = ["確定","取消"];
mui.confirm('確認關閉當前窗口?','Hello MUI',btn,function(e){
if(e.index==0){
//執行mui封裝好的窗口關閉邏輯;
old_back();
}
});
}
```
#### 為何設置了swipeBack: false,在iOS上依然可以右滑關閉?
iOS平臺原生支持從屏幕邊緣右滑關閉,這個是通過popGesture參數控制的,參考[5+規范](https://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewStyle),若需禁用,可通過setStyle方法設置popGesture為none。
#### 能否通過addEventListener增加back按鍵監聽實現自定義關閉邏輯?
addEventListener只會增加新的執行邏輯,老的監聽邏輯(mui.back)依然會執行,因此,若需實現自定義關閉邏輯,一定要重寫mui.back。
> 代碼塊激活字符:????mback
## 預加載
所謂的預加載技術就是在用戶尚未觸發頁面跳轉時,提前創建目標頁面,這樣當用戶跳轉時,就可以立即進行頁面切換,節省創建新頁面的時間,提升app使用體驗。mui提供兩種方式實現頁面預加載。
> 方式一:通過mui.init方法中的preloadPages參數進行配置.
```
mui.init({
preloadPages:[
{
url:prelaod-page-url,
id:preload-page-id,
styles:{},//窗口參數
extras:{},//自定義擴展參數
subpages:[{},{}]//預加載頁面的子頁面
}
],
preloadLimit:5//預加載窗口數量限制(一旦超出,先進先出)默認不限制
});
```
該種方案使用簡單、可預加載多個頁面,但不會返回預加載每個頁面的引用,若要獲得對應webview引用,還需要通過`plus.webview.getWebviewById`方式獲得;另外,因為mui.init是異步執行,執行完mui.init方法后立即獲得對應webview引用,可能會失敗,例如如下代碼:
```
mui.init({
preloadPages:[
{
url:'list.html',
id:'list'
}
]
});
var list = plus.webview.getWebviewByid('list');//這里可能返回空;
```
> 子頁和預加載頁設置(列表頁顯示具體內容)

> 獲得預加載頁(手機上都是webview對象)

> 方式二:通過mui.preload方法預加載.
```
var page = mui.preload({
url:new-page-url,
id:new-page-id,//默認使用當前頁面的url作為id
styles:{},//窗口參數
extras:{}//自定義擴展參數
});
```
通過`mui.preload()`方法預加載,可立即返回對應webview的引用,但一次僅能預加載一個頁面;
若需加載多個webview,則需多次調用`mui.preload()`方法;
如上兩種方案,各有優劣,需根據具體業務場景靈活選擇;
## 判斷預加載是否成功
### 方式一、通過直觀現象分析
> 預加載頁面會立即打開,不會顯示等待框;非預加載頁面默認會先顯示等待框,再顯示新頁面;
### 方式二、增加log分析預加載頁面是否已創建
比如:A頁面中預加載B頁面,則在A頁面完全加載(可通過setTimeout模擬)后,打印當前應用所有webview,看是否包含B頁面的url,以此來分析
例如:在A頁面增加如下代碼:
```
mui.plusReady(function(){
setTimeout(function(){
var array = plus.webview.all();
if(array){
for(var i=0,len=array.length;i<len;i++){
console.log(array[i].getURL());
}
}
},5000)
});
```
- Layer無刷新不跳轉彈框提示信息
- 整合ThinkPHP+實用代碼
- TP整合Layer插件實現無刷新
- 自定義助手函數
- 添加信息失敗后不跳轉
- 三種無限級分類
- TP常用代碼
- 自定義公共函數
- TP模型管理專題
- TP模型管理之添加模型
- sfox_newmodel.sql
- TP模型管理之刪除模型
- TP模型管理之編輯模型
- TP模型管理之字段添加
- sfox_newmodel.sql_edit
- layer_hplus.js_edit
- TP模型管理之字段刪除
- TP模型管理之字段編輯
- TP模型管理之預覽模型
- TP模型管理之公共函數
- layer_hplus.js_修訂一
- TP模型管理之預覽模型靜態頁
- 后臺內容管理系統
- 分類樹顯示
- 內容列表顯示
- 信息發布
- 編輯信息
- layer_hplus.js
- myJs第一版
- myJs第二版
- myJs第三版
- myJs第四版
- TP5插件用法
- Datatables
- WebUploader
- bootstrap-fileinput
- UEditor
- 簡單調用
- 路徑問題
- 跨域多圖上傳
- 跨域單圖上傳
- UEditor圖片跨域上傳解決方案
- 定制工具欄圖標
- ajaxFileUpload
- LayUI
- 圖片上傳
- layui分頁
- 搜索頁
- 搜索優化及刪除
- Uploadify
- TP5前端應用
- 靜態首頁
- 前臺首頁功能實現
- 自定義標簽庫
- 前臺模板繼承應用
- 首頁自定義標簽改進
- 文章內容頁
- 自定義標簽改進
- 自定義標簽修正
- 圖片等比例自動縮放
- 后臺權限管理
- 角色管理
- 規則管理
- 權限設置
- 會員管理
- 權限管理
- 前臺登錄注冊功能
- 注冊登錄
- 阿里大于手機注冊
- 阿里大于升級阿里云短信服務
- 自動登錄完成
- PHP異位或加密實現自動登陸
- 微信開發
- 分享接口
- 靜態頁面實現微信分享
- 動態頁微信分享
- 頁面靜態化
- 1-全站靜態化前期配置
- 2-鏈接地址靜態化
- TP5常用片段代碼
- 加載靜態資源路徑與常量
- thinkphp5預定義常量
- 刪除某文件夾的內容
- 解壓插件包
- 異步提交插件
- 其他功能
- 背景音樂
- 手機訪問PC網站自動跳轉到手機網站代碼
- 手機微信音樂MP3播放器
- 后盾之網頁背景音樂
- 播放器寬度自適應
- 前臺首頁數據調用
- 視頻列表
- 搜索分頁
- H5解決蘋果(IOS)不能自動播放音樂
- 清空緩存
- 文件處理常識
- 刪除路徑下的所有文件夾和文件
- 一鍵清空緩存
- 評論留言
- 格式化時間
- 替換微博內容的URL地址@用戶與表情
- PHP正則理解
- jQuery評論插件
- TP空操作
- TP路由
- 跨域訪問
- 設置請其頭允許跨域請求
- 模板前臺判斷手機訪問跳轉手機網址代碼
- PHP遍歷一個文件夾下所有文件和子文件夾
- PHP獲取視頻的第一幀與時長
- TP5數據庫
- 鏈式操作原理
- update替換字段部分內容
- 后臺開發
- 后臺登錄頁居中顯示
- TP5自帶驗證碼
- JS & JQuery專題
- 二級城市聯動菜單
- 模板引擎
- 混合模板編譯
- 黃永成TP微博開發
- 消息推送
- memcache安裝
- 插件開發
- 插件介紹
- 插件鉤子
- 淺談初步理解鉤子
- 插件鉤子(hooks)分析
- 插件鉤子簡單理解
- 控制器調用插件
- 鉤子通用處理函數
- 插件基類代碼
- 插件測試代碼
- 淺談鉤子與插件
- 技術綜合
- 常用代碼
- PHP
- 56個PHP開發常用代碼片段(上)
- 56個PHP 開發常用代碼片段(中)
- 56個PHP 開發常用代碼片段(下)
- sublime text安裝自動補全注釋的插件
- 影音視頻開發
- 視頻
- H5視頻直播掃盲
- 音樂
- 語音
- PHP實現語音播報功能
- MUI
- 窗體操作