小程序提供了路由功能來實現(xiàn)頁面跳轉(zhuǎn),但是在使用的過程中我們還是發(fā)現(xiàn)有些不方便的地方,通過封裝蕊肥,我們可以實現(xiàn)諸如路由管理、簡化api等功能蛤肌。
頁面的跳轉(zhuǎn)存在哪些問題呢壁却?
- 與接口的調(diào)用一樣面臨url的管理問題;
- 傳遞參數(shù)的方式不太友好裸准,只能拼裝url展东;
- 參數(shù)類型單一,只支持string炒俱。
alias
第一個問題很好解決盐肃,我們做一個集中管理爪膊,比如新建一個router/routes.js
文件來實現(xiàn)alias:
// routes.js
module.exports = {
// 主頁
home: '/pages/index/index',
// 個人中心
uc: '/pages/user_center/index',
};
然后使用的時候變成這樣:
const routes = require('../../router/routes.js');
Page({
onReady() {
wx.navigateTo({
url: routes.uc,
});
},
});
query
第二個問題,我們先來看個例子砸王,假如我們跳轉(zhuǎn)pages/user_center/index
頁面的同時還要傳userId
過去推盛,正常情況下是這么來操作的:
const routes = require('../../router/routes.js');
Page({
onReady() {
const userId = '123456';
wx.navigateTo({
url: `${routes.uc}?userId=${userId}`,
});
},
});
這樣確實不好看,我能不能把參數(shù)部分單獨拿出來谦铃,不用拼接到url上呢耘成?
可以,我們試著實現(xiàn)一個navigateTo
函數(shù):
const routes = require('../../router/routes.js');
function navigateTo({ url, query }) {
const queryStr = Object.keys(query).map(k => `${k}=${query[k]}`).join('&');
wx.navigateTo({
url: `${url}?${queryStr}`,
});
}
Page({
onReady() {
const userId = '123456';
navigateTo({
url: routes.uc,
query: {
userId,
},
});
},
});
嗯驹闰,這樣貌似舒服一點瘪菌。
參數(shù)保真
第三個問題的情況是,當(dāng)我們傳遞的參數(shù)argument不是string
嘹朗,而是number
或者boolean
時师妙,也只能在下個頁面得到一個string
值:
// pages/index/index.js
Page({
onReady() {
navigateTo({
url: routes.uc,
query: {
isActive: true,
},
});
},
});
// pages/user_center/index.js
Page({
onLoad(options) {
console.log(options.isActive); // => "true"
console.log(typeof options.isActive); // => "string"
console.log(options.isActive === true); // => false
},
});
上面這種情況想必很多人都遇到過,而且感到很抓狂骡显,本來就想傳遞一個boolean疆栏,結(jié)果不管傳什么都會變成string。
有什么辦法可以讓數(shù)據(jù)變成字符串之后惫谤,還能還原成原來的類型?
好熟悉珠洗,這不就是json嗎溜歪?我們把要傳的數(shù)據(jù)轉(zhuǎn)成json字符串(JSON.stringify
),然后在下個頁面把它轉(zhuǎn)回json數(shù)據(jù)(JSON.parse
)不就好了嘛许蓖!
我們試著修改原來的navigateTo
:
const routes = require('../../router/routes.js');
function navigateTo({ url, data }) {
const dataStr = JSON.stringify(data);
wx.navigateTo({
url: `${url}?jsonStr=${dataStr}`,
});
}
Page({
onReady() {
navigateTo({
url: routes.uc,
data: {
isActive: true,
},
});
},
});
這樣我們在頁面中接受json字符串并轉(zhuǎn)換它:
// pages/user_center/index.js
Page({
onLoad(options) {
const json = JSON.parse(options.jsonStr);
console.log(json.isActive); // => true
console.log(typeof json.isActive); // => "boolean"
console.log(json.isActive === true); // => true
},
});
這里其實隱藏了一個問題蝴猪,那就是url的轉(zhuǎn)義,假如json字符串中包含了類似?
膊爪、&
之類的符號自阱,可能導(dǎo)致我們參數(shù)解析出錯,所以我們要把json字符串encode一下:
function navigateTo({ url, data }) {
const dataStr = encodeURIComponent(JSON.stringify(data));
wx.navigateTo({
url: `${url}?encodedData=${dataStr}`,
});
}
// pages/user_center/index.js
Page({
onLoad(options) {
const json = JSON.parse(decodeURIComponent(options.encodedData));
console.log(json.isActive); // => true
console.log(typeof json.isActive); // => "boolean"
console.log(json.isActive === true); // => true
},
});
這樣使用起來不方便米酬,我們封裝一下沛豌,新建文件router/index.js
:
const routes = require('./routes.js');
function navigateTo({ url, data }) {
const dataStr = encodeURIComponent(JSON.stringify(data));
wx.navigateTo({
url: `${url}?encodedData=${dataStr}`,
});
}
function extract(options) {
return JSON.parse(decodeURIComponent(options.encodedData));
}
module.exports = {
routes,
navigateTo,
extract,
};
頁面中我們這樣來使用:
const router = require('../../router/index.js');
// page home
Page({
onLoad(options) {
router.navigateTo({
url: router.routes.uc,
data: {
isActive: true,
},
});
},
});
// page uc
Page({
onLoad(options) {
const json = router.extract(options);
console.log(json.isActive); // => true
console.log(typeof json.isActive); // => "boolean"
console.log(json.isActive === true); // => true
},
});
route name
這樣貌似還不錯,但是router.navigateTo
不太好記赃额,router.routes.uc
有點冗長加派,我們考慮把navigateTo
換成簡單的push
,至于路由跳芳,我們可以使用name
的方式來替換原來url
參數(shù):
const routes = require('./routes.js');
function push({ name, data }) {
const dataStr = encodeURIComponent(JSON.stringify(data));
const url = routes[name];
wx.navigateTo({
url: `${url}?encodedData=${dataStr}`,
});
}
function extract(options) {
return JSON.parse(decodeURIComponent(options.encodedData));
}
module.exports = {
push,
extract,
};
在頁面中使用:
const router = require('../../router/index.js');
Page({
onLoad(options) {
router.push({
name: 'uc',
data: {
isActive: true,
},
});
},
});
navigateTo or switchTab
頁面跳轉(zhuǎn)除了navigateTo之外還有switchTab芍锦,我們是不是可以把這個差異抹掉?答案是肯定的飞盆,如果我們在配置routes的時候就已經(jīng)指定是普通頁面還是tab頁面娄琉,那么程序完全可以切換到對應(yīng)的跳轉(zhuǎn)方式次乓。
我們修改一下router/routes.js
,假設(shè)home是一個tab頁面:
module.exports = {
// 主頁
home: {
type: 'tab',
path: '/pages/index/index',
},
uc: {
path: '/pages/a/index',
},
};
然后修改router/index.js
中push
的實現(xiàn):
function push({ name, data }) {
const dataStr = encodeURIComponent(JSON.stringify(data));
const route = routes[name];
if (route.type === 'tab') {
wx.switchTab({
url: `${route.path}`, // 注意tab頁面是不支持傳參的
});
return;
}
wx.navigateTo({
url: `${route.path}?encodedData=${dataStr}`,
});
}
搞定孽水,這樣我們一個router.push
就能自動切換兩種跳轉(zhuǎn)方式了檬输,而且之后一旦頁面類型有變動,我們也只需要修改route
的定義就可以了匈棘。
直接尋址
alias用著很不錯丧慈,但是有一點挺麻煩得就是每新建一個頁面都要寫一個alias,即使沒有別名的需要主卫,我們是不是可以處理一下逃默,如果在alias沒命中,那就直接把name轉(zhuǎn)化成url簇搅?這也是闊以的完域。
function push({ name, data }) {
const dataStr = encodeURIComponent(JSON.stringify(data));
const route = routes[name];
const url = route ? route.path : name;
if (route.type === 'tab') {
wx.switchTab({
url: `${url}`, // 注意tab頁面是不支持傳參的
});
return;
}
wx.navigateTo({
url: `${url}?encodedData=${dataStr}`,
});
}
在頁面中使用:
Page({
onLoad(options) {
router.push({
name: 'pages/user_center/a/index',
data: {
isActive: true,
},
});
},
});
注意,為了方便維護(hù)瘩将,我們規(guī)定了每個頁面都必須存放在一個特定的文件夾吟税,一個文件夾的當(dāng)前路徑下只能存在一個index頁面,比如
pages/index
下面會存放pages/index/index.js
姿现、pages/index/index.wxml
肠仪、pages/index/index.wxss
、pages/index/index.json
备典,這時候你就不能繼續(xù)在這個文件夾根路徑存放另外一個頁面异旧,而必須是新建一個文件夾來存放,比如pages/index/pageB/index.js
提佣、pages/index/pageB/index.wxml
吮蛹、pages/index/pageB/index.wxss
、pages/index/pageB/index.json
拌屏。
這樣是能實現(xiàn)功能潮针,但是這個name怎么看都跟alias風(fēng)格差太多,我們試著定義一套轉(zhuǎn)化規(guī)則倚喂,讓直接尋址的name與alias風(fēng)格統(tǒng)一一些每篷,pages
和index
其實我們可以省略掉,/
我們可以用.
來替換务唐,那么原來的name就變成了user_center.a
:
Page({
onLoad(options) {
router.push({
name: 'user_center.a',
data: {
isActive: true,
},
});
},
});
我們再來改進(jìn)router/index.js
中push
的實現(xiàn):
function push({ name, data }) {
const dataStr = encodeURIComponent(JSON.stringify(data));
const route = routes[name];
const url = route ? route.path : `pages/${name.replace(/\./g, '/')}/index`;
if (route.type === 'tab') {
wx.switchTab({
url: `${url}`, // 注意tab頁面是不支持傳參的
});
return;
}
wx.navigateTo({
url: `${url}?encodedData=${dataStr}`,
});
}
這樣一來雳攘,由于支持直接尋址,跳轉(zhuǎn)home和uc還可以寫成這樣:
router.push({
name: 'index', // => /pages/index/index
});
router.push({
name: 'user_center', // => /pages/user_center/index
});
這樣一來枫笛,除了一些tab頁面以及特定的路由需要寫alias之外吨灭,我們也不需要新增一個頁面就寫一條alias這么麻煩了。
其他
除了上面介紹的navigateTo和switchTab外刑巧,其實還有wx.redirectTo
喧兄、wx.navigateBack
以及wx.reLaunch
等无畔,我們也可以做一層封裝,過程雷同吠冤,所以我們就不再一個個介紹浑彰,這里貼一下最終簡化后的api以及原生api的映射關(guān)系:
router.push => wx.navigateTo
router.replace => wx.redirectTo
router.pop => wx.navigateBack
router.relaunch => wx.reLaunch
最終實現(xiàn)已經(jīng)在發(fā)布在github上,感興趣的朋友可以移步了解:mp-router拯辙。