我們在進行頁面跳轉時,很多情況下都得考慮登入狀態問題,比如進入個人資訊頁面,下單交易頁面等等。在這些場景下,通常在頁面跳轉前,會先判斷下使用者是否已經登入,若已登入,則跳轉到相應的目標頁面,若沒有登入,則先跳轉到登入頁面,然後等著獲取登入狀態,若登入頁面關閉時,能獲取到已登入,則繼續跳轉到目標頁,若使用者取消了登入,則終止後面的行為。這樣的處理通常會存在一些問題,例如很多頁面都與登入狀態相關,這樣需要在大量的入口處增加登入邏輯判斷。即使封裝成一個方法,也需要關心是否登入成功,增加了邏輯的複雜性,而且登入頁面先關閉,再開啟新頁面,頁面切換動畫也很不協調。
那麼我們有沒有一種更好的方案來處理登入鑑權問題呢?首先我們先梳理一下我們想要的效果,我們的目的是要跳轉到相應的目標頁,目標頁是否需要先登入,我們是不太願意關注的,最好是內部自己處理掉,,若沒有登入,就先進行登入,登入成功後,繼續後面的行為,外面使用的地方儘量做到無感知。總結一下就是進行頁面跳轉時,內部先判斷一下狀態,然後再進行後續的行為,而這恰好是Navigation攔截器的功能。
Navigation攔截器的介紹與使用
NavPathStack提供了setInterception方法,用於設定Navigation頁面跳轉攔截回撥。該方法需要傳一個NavigationInterception物件,該物件包含三個回撥函式willShow,didShow和modeChange,我們在willShow頁面即將顯示時,進行攔截處理。先判斷是否登入,沒有登入,就重定向到登入頁面,若已登入,則繼續後續行為,不做攔截。示例如下
@Entry
@ComponentV2
struct Index {
nav: NavPathStack = new NavPathStack()
isLogin: boolean = false
aboutToAppear(): void {
this.nav.setInterception({
willShow: (from: NavDestinationContext | NavBar, to: NavDestinationContext | NavBar,
operation: NavigationOperation, isAnimated: boolean) => {
if (typeof to === 'object') {
if (isLogin) {
AppRouter.popPage()
AppRouter.jumpPage('login', undefined)
}
}
}
})
}
build() {
Navigation(this.nav)
.hideToolBar(true)
.hideTitleBar(true)
.height('100%')
.width('100%')
}
}
攔截器細節最佳化
如何判斷是否需要進行攔截
在攔截器中,雖然我們可以進行攔截重定向跳轉,但需要考慮的一個問題是什麼情況下進行攔截,也就是哪些頁面跳轉時需要先判斷下登入狀態。首先想到的是弄一個陣列,所有需要登入校驗的頁面都放到這個陣列中。頁面跳轉時,我們只需要判斷下目標頁是否在陣列中,就可以知道是否需要進行攔截校驗登入了。其實思想是對的,只是我們有更簡單的實現方式。在系統路由表中,有一個data欄位,可以在這個欄位中增加一個欄位,是否需要登入,在攔截器中先獲取目標頁中這個引數,只要所有需要登入的頁面,都新增了這個欄位就可以了。我們以使用者資訊頁為例,配置如下
{
"routerMap": [
{
"name": "login",
"pageSourceFile": "src/main/ets/pages/login/LoginPage.ets",
"buildFunction": "loginBuilder"
},
{
"name": "user_info",
"pageSourceFile": "src/main/ets/pages/user/UserInfoPage.ets",
"buildFunction": "userInfoBuilder",
"data": {
"needLogin": "1"
}
}
]
}
攔截器中獲取該欄位的方式如下
this.nav.setInterception({
willShow: (from: NavDestinationContext | NavBar, to: NavDestinationContext | NavBar,
operation: NavigationOperation, isAnimated: boolean) => {
if (typeof to === 'object') {
const data = (to as NavDestinationContext).getConfigInRouteMap()?.data
if (data !== undefined && (data as object)['needLogin'] === '1' && !AppConstant.hasLogin) {
AppRouter.popPage()
AppRouter.jumpPage(Pages.login, undefined)
}
}
}
})
登入成功後如何獲取目標頁和頁面引數
登入成功後,我們如何知道要跳轉到哪個目標頁,以及跳轉到目標頁時所需要的引數呢?我們在跳轉到登入頁時可以增加2個引數targetPage和targetParam,分別表示要處理的目標頁以及相應的引數,若targetPage的值為undefined,則說明登入成功後沒有後續操作,若有值,則跳轉到這個頁面並把相應的引數傳過去。在攔截器中,可以透過to.pathInfo.name獲取到目標頁的名稱name以及透過to.pathInfo.param獲取到目標頁所需要的引數,並把它們賦值給登入頁面的targetPage和targetParam就行了。
我們可以發現使用攔截器這種方式,完全符合我們最初的設想,外部呼叫時不用考慮是否要校驗登入狀態,由攔截器內部自己處理。登入後也是直接跳轉到目標也,沒有頁面關閉效果。而且是否需要判斷登入,只需配置一個欄位就行了,非常方便。