單頁路由可以實現(xiàn)對路由的復(fù)雜操作, 彌補多頁開發(fā)的不足, 在體驗上, 效果也會更佳. 每一個單頁都是一個組件.
單頁的原理是通過ajax請求,把其它頁面以局部加載的方式,渲染到路由里面, 那頁面重復(fù)加載就勢必會導(dǎo)致ID重復(fù), 必須遵守以下幾個原則.
router.$
替換 $
選擇器;
例如:
$("#id").text()
改成 router.$("#id").text()
; $("#id").click(function(){})
改成 router.$("#id").click(function(){})
才能確保你操作的是當(dāng)前頁面選擇器.$.each
$.extend
... 這類非選擇器的操作無需改變;bui.load
繼續(xù)跳轉(zhuǎn); 可以了解下登錄權(quán)限./pages/ui/list.html
跟 pages/ui/list.html
是不一樣的, 在打包以后, /
開頭的文件將會從內(nèi)存的根目錄開始查找, 會導(dǎo)致404, 應(yīng)該統(tǒng)一使用相對 index.html
路徑的寫法 pages/ui/list.html
.index.html#+模塊名
的方式進(jìn)行預(yù)覽. index.html#pages/ui_controls/bui.list
使用
buijs
命令行構(gòu)建. 建議版本在1.6.0以上
, 如何使用buijs命令行工具?
## 創(chuàng)建目錄
$ mkdir demo
## 進(jìn)入demo目錄
$ cd demo
## 創(chuàng)建單頁工程
$ buijs create
## 安裝工程依賴
$ npm install
## 運行服務(wù)并預(yù)覽
$ npm run dev
目錄說明:
目錄名 | 描述 | 是否必須 |
---|---|---|
/app.json | 插件及跨域的配置 | 否 |
/gulpfile.js | gulp編譯配置 | 否 |
/package.json | 工程依賴配置 | 否 |
/src/index.html | 應(yīng)用首頁入口文件 | 是 |
/src/index.js | 路由的初始化腳本及全局事件 | 是 |
/src/css/ | 應(yīng)用樣式及bui.css樣式 | 是 |
/src/font/ | bui.css用到的字體圖標(biāo) | 是 |
/src/images/ | 應(yīng)用圖片目錄 | 否 |
/src/js/ | 應(yīng)用腳本 | 是 |
/src/js/zepto.js | bui.js默認(rèn)依賴于zepto.js 或 jquery | 是 |
/src/js/bui.js | BUI交互控件庫 | 是 |
/src/pages/ | 應(yīng)用的模塊 | 是 |
/src/pages/main/ | 默認(rèn)路由初始化以后會先載入這個main模塊 | 是 |
/src/pages/main/main.html | main模塊的模板 | 是 |
/src/pages/main/main.js | main模塊的業(yè)務(wù)腳本 | 是 |
打開編輯 src/index.html
, body 下只有一個div,這個便是路由最外層結(jié)構(gòu).
src/index.html
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<title>BUI單頁工程</title>
<meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link rel="stylesheet" href="css/bui.css" />
</head>
<body>
<!-- 第1步: 開啟單頁路由 -->
<div id="bui-router"></div>
<script src="js/zepto.js"></script>
<script src="js/bui.js"></script>
<!-- 初始化單頁 -->
<script src="index.js"></script>
</body>
</html>
src/index.js
// 把路由實例化給 window.router
window.router = bui.router();
bui.ready(function(){
// 加載頁面到div容器里面, 更多參數(shù)請查閱API
router.init({
id: "#bui-router"
})
})
當(dāng)路由初始化以后,會自動查找首頁
main
模塊, 這個模塊是內(nèi)部定義好的, 默認(rèn)指向路徑pages/main/main.html
并自動加載
相同名字的js文件
. 除了main
, 正常我們都是創(chuàng)建匿名模塊,這樣只要通過路徑跳轉(zhuǎn),就會自動加載相對路徑同名的模塊.
src/pages/main/main.html
<div class="bui-page">
<header class="bui-bar">
<div class="bui-bar-left">
</div>
<div class="bui-bar-main">main</div>
<div class="bui-bar-right">
</div>
</header>
<main>
<div id="btn" class="bui-btn">跳轉(zhuǎn)page2.html</div>
</main>
<footer></footer>
</div>
src/pages/main/main.js
loader.define(function(require,exports,module){
// 綁定按鈕跳轉(zhuǎn)
$("#btn").on("click",function(){
bui.load({ url: "pages/page2/page2.html", param: {} });
})
})
我們再創(chuàng)建一個
src/pages/page2/page2.html
,及src/pages/page2/page2.js
,那通過上面的方式跳轉(zhuǎn),則創(chuàng)建了一個src/pages/page2/page2
的模塊.
src/pages/page2/page2.html
<div class="bui-page">
<header class="bui-bar">
<div class="bui-bar-left">
</div>
<div class="bui-bar-main">page2</div>
<div class="bui-bar-right">
</div>
</header>
<main></main>
<footer></footer>
</div>
src/pages/page2/page2.js
loader.define(function(require,exports,module){
// 腳本都需要在這里執(zhí)行
})
注意:
router.back
;頁面跳轉(zhuǎn),保持js跟html一致的命名,才能自動加載觸發(fā). 同樣支持
bui.btn
靜態(tài)跳轉(zhuǎn)
參數(shù): option是一個對象
string
相對路徑
object
傳給目標(biāo)頁面的參數(shù)
例子:
router.load({ url: "pages/page2/page2.html", param: {} });
// 路由初始化以后,下面的跳轉(zhuǎn)也是單頁跳轉(zhuǎn)
bui.load({ url: "pages/page2/page2.html", param: {} });
注意: 仔細(xì)查看下跟多頁路由接口都是保持的一致,甚至你可以直接使用
bui.load
來代替router.load
, 這個也是最早我們推薦的方式, 所以你會看到部分例子還是保持這樣的寫法.
接收到的參數(shù)為一個對象, 無需在回調(diào)里面拿.
例子:
var params = router.getPageParams();
router.back 跟 bui.back 的區(qū)別在于, bui.back 后退會把歷史記錄一起后退,router.back 只是后退頁面. 開發(fā)中推薦使用
bui.load
及bui.back
.
參數(shù): option是一個對象
number
后退幾層,默認(rèn):-1
string
后退到指定的模塊名, name跟index,只能有一個
function
后退以后執(zhí)行回調(diào)
例子:
// 普通后退
router.back();
// 后退局部刷新
router.back({
callback: function(module){
// 后退的頁面有拋出一個init方法 或者刷新方法, 做內(nèi)容修改
module.init();
}
});
// 后退2層刷新
bui.back({
index: -2,
callback: function(){
bui.refresh()
}
});
// 不管在哪層,都可以后退到首頁
bui.back({
name: "main"
});
頁面替換不會增加歷史記錄
參數(shù): option是一個對象
string
相對路徑
object
傳給目標(biāo)頁面的參數(shù)
例子:
router.replace({ url: "pages/page3/page3.html" });
參數(shù): option是一個對象
string|object
局部的id或者對象
string
相對路徑
object
傳給目標(biāo)頁面的參數(shù)
例子:
router.loadPart({ id:"#part", url: "pages/page2/page2.html", param: {} });
局部加載在1.6.x 有更簡單的方式, 使用 component標(biāo)簽, 便可自動加載.
<component name="pages/page2/page2"></component>
參數(shù):
string
模塊的名稱
例子:
loader.define(function(require,exports,module) {
// module 為當(dāng)前的模塊信息
var pid = module.moduleName,
params = router.getPartParams(pid);
})
如果你的應(yīng)用是webapp,那這個功能就比較好用了,先在首頁預(yù)加載想要觸發(fā)的頁面, 當(dāng)點擊觸發(fā)相同頁面的時候, 你會發(fā)現(xiàn)跳轉(zhuǎn)速度快多了, 本地應(yīng)用則無需使用到預(yù)加載.
參數(shù): option是一個對象
string
緩存模板
string
緩存腳本
// 預(yù)加載一個頁面
router.preload({ url: "pages/page2/page2.html" });
// 預(yù)加載多個頁面
router.preload([{
url: "pages/page2/page2.html"
},{
url: "pages/page3/page3.html"
}]);
頁面重復(fù)加載的表現(xiàn)通常是, 渲染出不來,要刷新才有數(shù)據(jù). 事件被重復(fù)綁定等.
router.$
跟$
的區(qū)別在于, router.$ 是相對于當(dāng)前路由的頁面查找, 有一種情況, 頁面需要被重復(fù)加載, 比方列表頁,跳轉(zhuǎn)到詳情頁,詳情頁又有推薦的列表,點擊又會跳轉(zhuǎn)到詳情, 這種時候,$
綁定頁面的事件會被重復(fù)綁定,router.$
則不會.
事件綁定示例:
// 靜態(tài)的結(jié)構(gòu)綁定事件
router.$(".bui-page .btn").click(function(e){
})
另外,建議一個單頁應(yīng)用的層級建議控制在 5級左右. 已經(jīng)加載過的頁面,使用后退刷新處理. 例如:
// 推薦: 這也是微信小程序里面推薦的層級數(shù)
A->B->C->D->E
// 不要這樣, 這種做法本身會讓用戶陷入混亂
A->B->C->B->C
頁面加載,在模板加載完成就會觸發(fā),有緩存的時候只觸發(fā)一次
router.on("load",function(e){
// 獲取當(dāng)前頁的模塊
console.log(e.target);
// 獲取上一頁的模塊
console.log(e.prevTarget);
})
頁面完成,每次加載完模板都會觸發(fā)
router.on("complete",function(e){
// 獲取當(dāng)前頁的模塊
console.log(e.target);
// 獲取上一頁的模塊
console.log(e.prevTarget);
})
router.on("back",function(e){
// 獲取當(dāng)前頁的模塊
console.log(e.target);
// 獲取上一頁的模塊
console.log(e.prevTarget);
})
router.on("refresh",function(e){
// 獲取當(dāng)前頁的模塊
console.log(e.target);
// 獲取上一頁的模塊
console.log(e.prevTarget);
})
頁面局部加載后觸發(fā)
router.on("loadpart",function(e){
// 獲取當(dāng)前頁子模塊
console.log(e.target);
// 獲取當(dāng)前頁父模塊
console.log(e.prevTarget);
})
注意: 建議全局事件都在 index.js 加載. 事件名全部為小寫.
這是路由加載的完整過程.
bui.ready
;bui.ready
里面執(zhí)行;router.$
替換 $
,確保你操作的是當(dāng)前頁面的模塊的dom;bui.loader
需要在 window.router
之前;1.5.3 新增, 路由跳轉(zhuǎn)頁面會觸發(fā)模塊的加載, 從進(jìn)入頁面,跳轉(zhuǎn)頁面,后退頁面完成一個頁面的生命周期.
這是打開路由第一次加載 main模塊以后觸發(fā)的加載.
1, main模塊
① 未定義生命周期, 只執(zhí)行 loaded .
2, main->A 頁面
① A頁面定義了生命周期, 從 beforeCreate -> created -> beforeLoad -> loaded -> show
, 這是A頁面的顯示生命周期. ② 從main->第2次跳轉(zhuǎn)到A頁面的時候, 直接走 beforeLoad -> loaded -> show
;
3, A頁面->B頁面, 會執(zhí)行 A頁面自身的 hide 再跳去 B頁面的 ① beforeCreate -> created -> beforeLoad -> loaded -> show
B->A
頁面 B跟A都有自己的生命周期, 后退的時候會先執(zhí)行 當(dāng)前頁的hide ->beforeDestroy -> destroyed
, 然后還會觸發(fā) A頁面的 show 方法.A->main
頁面 由于main沒有定義show方法, 所以只執(zhí)行A頁面的 hide ->beforeDestroy -> destroyed
具體查看模塊的定義. 頁面模塊的生命周期
接下來你可以繼續(xù)學(xué)習(xí)
更多建議: