微信小程序之自定義導航

背景

在開始以前呢,先提示一下你們,這裏所說的自定義導航指的是底部的tabbar!不要問我爲何,我也不知道。vue

其實我我的是不推薦自定義tabbar的,但爲何我仍是這麼去作了呢?主要緣由是咱們的業務須要根據客羣的不一樣展現不一樣的tabbar,若是用原生的tabbar是沒法實現的。所以咱們最後決定經過自定義的方式來實現。不想看文字也沒事,看一下個人示意圖你就知道需求了...╮(╯_╰)╭json

是否是秒懂!😎小程序

自定義導航的優劣勢

自定義導航最大的優點就是自定義,這不是廢話嗎...😂我總結了如下優點供你們參考:數組

  • 隨意組合tab的item成爲新的tabbar;
  • 能夠在導航上的寫一些動畫;
  • 在tabbar的UI上也可以更加自由;
  • ......

自定義導航的優點仁者見仁智者見智。可能由於業務的不一樣,所體現的優點也不同,我就不在這裏掰扯了。緩存

說了這些優點,其實自定義導航的劣勢也比較明顯:bash

  • 自定義導航須要在每一個tab頁面進行引入,麻煩。
  • 須要本身實現手機的適配。
  • 頁面切換的時候,會有閃現得狀況。
  • 必要的時候還須要考慮維護路由。
  • ......

實現方案

由於tabbar是一個比較獨立的模塊,因此能夠寫成組件的形式,這樣子也方便維護。引入組件方法要是不熟悉的話,你們能夠看官方的文檔。自定義組件微信

建立組件

目錄結構

這裏我把組件命名爲nav,目錄結構以下:app

能夠看到組件模塊的目錄結構和普通的page頁面沒什麼區別,固然page頁面須要在app.json裏邊進行引入,可是組件的話是不須要的。異步

json文件的配置

接下來,咱們須要在組件的json文件中配置一下:xss

{
  "component": true,
  "usingComponents": {}
}
複製代碼

其餘的就不須要再進行配置了。固然,假如你的組件裏邊須要引入其餘的組件,能夠用一樣的方式,而且在"usingComponents": {}裏邊引入就行。

wxml的結構

接下來就是wxml文件的書寫。這個頁面的話就和page頁面同樣就好了。這裏不須要考慮裏邊的節點數,由於最後小程序會把他們再封裝到一個根節點上,因此你們沒必要要再本身再添加一個根節點。

其餘的話,有一個地方可能須要和你們提醒一下,記得給iPhoneX作一下兼容!這裏個人tabbar用的是fixed的佈局方式,因此可能會遮住一些元素

<view class="nav-placeholder" style="padding-bottom: {{isIpx ? 68 : 0}}rpx;"></view>
複製代碼

我解決的方案如上,採用的是一個空白的元素進行佔位。

引用組件的時候須要在目標頁面的json文件中聲明一下:

"usingComponents": {
    "x-nav":"../../",
}
複製代碼

而後在模板中直接引用就好了:

<x-nav show="{{true}}" cartcount="{{cartcount}}" catchtouchmove="ture"></x-nav>
複製代碼

wxss的結構

這裏的樣式就不要再具體說明了,由於組件的存在因此裏邊的類名不會被污染,你們放心的使用,你們能夠在裏邊加各類動畫,好比下邊這樣:

這個只是一個思路,要是我真這麼設計,估計明天就會由於鞋帶是蝴蝶結的被開除。大家可讓設計作一些高大上的東西出來。

若是你非要用外部的樣式的話也能夠,可是你本身須要注意類名命名的問題,能夠經過聲明options來實現:

options:{
    addGlobalClass: true
},
複製代碼

js的結構

js的結構可能會有一些不同,由於是組件,因此再也不是經過page({})這樣子的方式去建立,而會改爲Component({})的方式。

其餘的寫法和vue就比較像了。

props就變成了properties,格式以下:

properties: {
	val1: {
		type: Boolean,
		value: false,
		observer: function(newVal, oldVal) {
			console.log(newVal, oldVal);
		}
	},
	val2: {
		type: String,
		value: '0',
		observer: function(newVal, oldVal) {
			console.log(newVal, oldVal);
		}
	},
	...
},
複製代碼

properties中假如你傳入的值可能會是不一樣類型的你能夠換成optionalTypes,這個是支持多類型的

data的話就不必細說了,小程序的組件裏邊依然支持getApp()的方法,假如你有什麼方法寫在app.js中也是能夠直接調用的。

在這裏邊我會用到show()方法,組件中使用onShow()這種帶on的生命週期是有一些特別的,以下:

pageLifetimes:{
	show(){
            consolelog('show');
	},
	hide(){
            consolelog('hide');
	},
	resize(){
            consolelog('hide');
	},
},
複製代碼

不帶on的話是寫在另一個對象中的lifetimes:{}

lifetimes: {
    attached() {
      // 在組件實例進入頁面節點樹時執行
    },
    detached() {
      // 在組件實例被從頁面節點樹移除時執行
    },
    created() {
      // 在組件實例剛剛被建立的時候
    },
    ready() {
      // 在組件在視圖層佈局完成後執行
    },
    moved() {
      // 在組件實例被移動到節點樹另外一個位置時執行
    },
    error() {
      // 每當組件方法拋出錯誤時執行
    }
  },
複製代碼

這裏可能會有同窗有疑問這個lifetimes裏的方法爲何能夠寫在外邊,其實寫在外邊的是舊式的定義方式,能夠保持對 <2.2.3> 版本基礎庫的兼容。

其餘的地方有一個navList:[]這個就是導航tab的list了,格式的話,推薦你們能夠模仿原生的格式:

navList: [
	{
		name:'***', //nav的名稱
		url:'****', //跳轉的地址
		actived:true, //是否選中狀態
		nomalImg:'***', //不被選中時的icon
		activedImg:'***', //被選中的icon
		type:'***' //導航類型
	},
	{
		name:'***',
		url:'***',
		actived:false,
		nomalImg:'***',
		activedImg:'***',
		type:'***'
	},
	{
		name:'***',
		url:'***',
		actived:false,
		nomalImg:'***',
		activedImg:'***',
		type:'***'
	}
],
複製代碼

這裏的話你們能夠優化一下,由於當時寫的比較急,再總結的時候發現好多地方能夠優化。好比這個navList你們能夠改成對象的模式,由於在getCurrentPages()中的路徑其實也是/pages/....這樣子的格式,你們能夠把路徑做爲key,這樣就不須要每次change的時候都去遍歷一次數組,媽耶,當時我怎麼就沒這麼寫...😭

還有一點思路能夠供給你們做爲參考,由於咱們在切換tab的時候須要把actived狀態進行修改,這個修改咱們能夠不用去過多的去考慮切換的過程,由於原生的切換是使用wx.switchTab的方式進行切換,咱們自定義導航的話也能夠模仿這個方式去作,咱們能夠wx.redirectTo來代替,由於用的是redirect,因此咱們在判斷路由的時候比較方便,直接取路由中的第一個來進行判斷就好了。

其實說到路由棧,有一個比較麻煩的地方就是,當這個tab頁面變成一個常規的頁面時(就是能夠經過navigate的方式訪問),咱們的訪問路徑就會複雜起來,舉個例子吧:

假如你經過點擊tab進行切換頁面的時候,這個是正常狀況,可是假如你從別的頁面訪問的時候這個路由的長度可能就會大於1,這個時候你就須要對路由進行更加細緻的檢查了。

其餘的話,我遇到的問題就是背景中提到的根據客羣的不一樣展現不一樣的tabbar

這個的問題點在於,咱們獲取用戶客羣信息的時候是個異步的過程,當請求回來的時候會有一個比較明顯的變換過程,可能導航就會從三個變成四個,這個的話,目前的個人方案沒有徹底避免,由於我不可能等到請求回來的時候再顯示導航條吧,因此我會把用戶第一次請求的結果記錄下來,以後的話會優先取緩存中的數據,這樣除了用戶第一次回變換一次,以後的話就不會出現閃爍的狀況

總結

你們在可使用小程序原生的tabbar的時候就用原生的,由於原生的體驗上會更好一些。固然,若是不得不用自定義導航的時候不如就好好設計一下,充分發揮自定義導航的優點。要是你們有什麼別的想法,歡迎你們在底下留言,同時也歡迎你們加我微信交流:zyf348519452,交個朋友也很好啊😎

相關文章
相關標籤/搜索