【小程序】路由的封裝

小程序提供了路由功能來實現(xiàn)頁面跳轉(zhuǎn),但是在使用的過程中我們還是發(fā)現(xiàn)有些不方便的地方,通過封裝蕊肥,我們可以實現(xiàn)諸如路由管理、簡化api等功能蛤肌。

頁面的跳轉(zhuǎn)存在哪些問題呢壁却?

  1. 與接口的調(diào)用一樣面臨url的管理問題;
  2. 傳遞參數(shù)的方式不太友好裸准,只能拼裝url展东;
  3. 參數(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.jspush的實現(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.wxsspages/index/index.json备典,這時候你就不能繼續(xù)在這個文件夾根路徑存放另外一個頁面异旧,而必須是新建一個文件夾來存放,比如pages/index/pageB/index.js提佣、pages/index/pageB/index.wxml吮蛹、pages/index/pageB/index.wxsspages/index/pageB/index.json拌屏。

這樣是能實現(xiàn)功能潮针,但是這個name怎么看都跟alias風(fēng)格差太多,我們試著定義一套轉(zhuǎn)化規(guī)則倚喂,讓直接尋址的name與alias風(fēng)格統(tǒng)一一些每篷,pagesindex其實我們可以省略掉,/我們可以用.來替換务唐,那么原來的name就變成了user_center.a

Page({
  onLoad(options) {
    router.push({
      name: 'user_center.a',
      data: {
        isActive: true,
      },
    });
  },
});

我們再來改進(jìn)router/index.jspush的實現(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拯辙。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末郭变,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子涯保,更是在濱河造成了極大的恐慌诉濒,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夕春,死亡現(xiàn)場離奇詭異未荒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)及志,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門片排,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人速侈,你說我怎么就攤上這事率寡。” “怎么了锌畸?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵勇劣,是天一觀的道長。 經(jīng)常有香客問我潭枣,道長,這世上最難降的妖魔是什么幻捏? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任盆犁,我火速辦了婚禮,結(jié)果婚禮上篡九,老公的妹妹穿的比我還像新娘谐岁。我一直安慰自己,他們只是感情好榛臼,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布伊佃。 她就那樣靜靜地躺著,像睡著了一般沛善。 火紅的嫁衣襯著肌膚如雪航揉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天金刁,我揣著相機(jī)與錄音帅涂,去河邊找鬼议薪。 笑死,一個胖子當(dāng)著我的面吹牛媳友,可吹牛的內(nèi)容都是我干的斯议。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼醇锚,長吁一口氣:“原來是場噩夢啊……” “哼哼御!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起焊唬,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤恋昼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后求晶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焰雕,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年芳杏,在試婚紗的時候發(fā)現(xiàn)自己被綠了矩屁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡爵赵,死狀恐怖吝秕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情空幻,我是刑警寧澤烁峭,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站秕铛,受9級特大地震影響约郁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜但两,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一鬓梅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谨湘,春花似錦绽快、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至擅耽,卻和暖如春活孩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秫筏。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工诱鞠, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留挎挖,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓航夺,卻偏偏與公主長得像蕉朵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子阳掐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容