浅谈小程序路由的封装设计
发布于 5 年前 作者 lqin 2701 次浏览 来自 分享

微信官方提供了基础的路由能力,在日常的开发中虽已够用,但随着开发的深入,会遇到许多值得思考提炼的问题。本文将探讨作者在微信小程序(以下简称小程序)开发当中遇到的问题,以及解决方案设计。

参考“WHY-HOW-WHAT"黄金圈思维法则,首先讲述为什么小程序的路由需要封装设计,也就是存在哪些问题,需要封装处理?

存在的问题

路由跳转的路径与文件路径耦合

小程序的路由跳转使用的是真实文件路径,因此若文件的结构发生变化,必会影响到所有的页面的跳转路径。

笔者在实际开发中就遇到这个问题,以小程序分包举例。

小程序的分包是以文件夹为单位的。如果要将一系列的页面拆分成分包,则需要将这些文件移至同个目录之下,因此必然导致路由的跳转路径发生变更。如果此时路由跳转均是直接通过文件路径跳转的话,则需要全局改动,导致的工作量不少。

另外,当开发团队比较庞大时,不同的业务之间总会存在互相跳转的情况。当其中一个页面地址发生变更时,其他业务跳转到该页面的路径都需要手动变更。若此时通知不及时,或者遗漏了一些地方,导致跳转失败,终会酿成大错。

路由传参

目前小程序支持的传参方式,即通过跳转路径上的query查询参数。

通过query传参的问题,与在Web上URL传参是一致的,比如:

  • query的参数长度有限
  • query只能传递可序列化的数据
  • 导航前需手动序列化,到达目标页面后需反序列化

导航守卫

在日常业务中,会存在一些页面需要一定条件才允许进入的。

举个例子,会员服务是一种很常见的能力,而会员中心的进入条件是:

  • 该用户已经完成登录
  • 该用户是本产品的会员

一般情况下,这有特定准入资格的页面的导航逻辑是这样的:

这种方式简单明了,但存在一个问题:需要每次跳转前主动判断,逻辑冗余以外,还可能被遗漏。

通过 导航守卫 的方式,在全局上注册“守卫”来保护导航,收拢“守卫”的逻辑,保证代码整洁的同时,还增加了导航的安全性。

思路

本文的小程序路由设计思路,即是面向解决问题设计。

通过提取实践遇到的问题,抽象处理,即可成为路由的底层的封装。

命名路由

使用 命名路由 的方式可以解决前文提及的跳转路径和文件真实路径耦合问题。

通过Map来映射 页面ID页面地址,路由跳转时,仅能使用 页面ID 进行路由跳转。

下面以导航至首页举例:

// before
wx.switchTab('pages/home/index')

// after
router.go('home')

由于小程序有tab页面和普通页面之分,因此导航至tab页时需使用switchTab

细心的读者可能会发现上文使用了go方法,而不是switchTab。其实,具体哪些页面属于tab页面,在app.json已经明确配置。对于使用者来说,不需要关心跳转的页面是属于哪种类型,这些细节都应该统一在底层封装好。下面罗列Router与官方API的对应关系:

Router API的设计原则是保持简单,以及尽量保持与web规范一致

传递参数

微信官方提供的query方式传参,若参数是普通数据类型(如NumberString)时可以直接使用;但若是涉及到复杂数据类型(如ArrayObject)时,需要先做序列化处理,当数据较为庞大时,性能的损耗还是比较明显的。

因此,在内存上传递参数是比较便利且容易想到的办法。

利用数据字典,将页面ID作为key、传递的参数作为value,写入Routerstate

router.go = function(pageID, params) {
    // do something...
    router.state[pageID] = params
}

在目标页面上,可以通过router.getParams()方法,获取传递的参数。

由于采用了命名路由的方式,可以使用页面ID作为key,避免了使用跳转路径做key时,涉及到的绝对与相对路径问题。

导航守卫

由于路由的能力是微信官方提供的,因此无法像 vue-router 那样提供多类型的导航守卫,但仅有全局导航守卫也足够使用。

全局注册前置导航守卫:

router.beforeEach((to, from) => {
    // do something...
})

其中,to是即将进入的路由对象,而from则是当前正要离开的路由对象。

路由对象包含:

  • pageID:页面ID
  • path:页面ID对应的path
  • params:传递的参数
  • query: URL的查询参数

配置信息

由前文提到的 命名路由 做法需要一个配置文件来关联页面ID页面路径的关系。

页面的配置信息,则是使用router.config.js设置,然后通过构建工具编译转成app.json

以下是route.config.js

其中,跳转首页则是router.go('home');而跳转分包health的首页则是使用router.go('health.home')

通过以上的配置文件,使用构建工具转换成微信官方可识别的app.json配置:

辅助函数

在日常开发当中,经常会用到一些和路由相关的通用辅助函数,如获取当前页面,获取上个页面等。这些辅助函数都应该统一抽象封装,避免代码冗余。

router.utils = {
    getCurPage() { // 获取当前页面信息
        let pages = getCurrentPages()
        let len = pages.length
        
        return pages[len - 1]
    },

    getPrePage() {}, // 获取上个页面信息

    getParams() {}, // 获取传递的参数

    getPageID(path) {} // 通过path找到pageID
}

navigator组件

微信官方除了提供API用于导航以外,还提供了navigator组件。

另外还有functional-page-navigator是用于插件当中,不能在小程序包使用,因此本文暂且将其忽略。

由于navigator的跳转参数仍是使用path,因此笔者将其进行二次封装,改造成可以通过pageID跳转。

总结

由于小程序相对比较封闭,因此在路由上能做的东西比较有限。

但路由又与许多概念有千丝万缕的关系。比如路由与文件结构关联,而文件结构又影响到分包的设计,环环相扣,影响到的地方则会越来越多。

因此,能提前看到本文提到的可能出现的问题,也许对后续的小程序开发有一定的参考意义。

另外,前文提到的很多问题,在早期开发,或者没有深入开发之前,都不会遇到。但是当你开始经历前文提到的那些问题时,往往此时的改造成本已经很大了。因此希望本文能给你带了一些启发,在早期规避这些问题,那本文的使命就达到了。

1 回复
回到顶部