V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
lblblong
V2EX  ›  分享创造

[开源] 一个 Taro 路由库:小程序的路由系统太难用了吧..

  •  
  •   lblblong · 2021-04-22 14:39:26 +08:00 · 1727 次点击
    这是一个创建于 474 天前的主题,其中的信息可能已经有所发展或是发生改变。

    小程序的路由有什么问题

    1. 路由跳转的页面 url 没有类型提示容易输错
    2. 路由传参需要手动拼接参数、无法携带任意类型、任意大小的数据
    3. 路由方法是异步的,页面通过 EventCannal 通信,事件的回调方法可读性差、耦合度高、只能在回调内部处理异常
    4. 路由跳转的鉴权等实现起来比较麻烦

    如何解决这些问题

    允许我先向你介绍: tarojs-router-next,这是一个 Taro 的路由辅助库,他提供以下特性帮助你方便的解决上面的问题

    • 自动生成带参数类型提示的路由方法
    • 允许传递任意类型、任意大小的参数数据
    • 同步的路由方法调用
    • koa 体验一致的路由中间件
    • 详细的文档

    1. 路由跳转的页面 url 没有类型提示容易输错

    tarojs-router-next 不需要使用者手写页面 url,它会监听项目 src/pages 内容变化,自动为使用者生成对应的路由方法并附加到 Router 类上,比如以下列子:

    左边的页面结构会生成右边的 Router.to** 系列方法,全都挂在 Router 类上

    2. 路由传参需要手动拼接参数、无法携带任意类型、任意大小的数据

    tarojs-router-next 允许直接传递一个对象给 params,它会把 params 展开拼接到 url 后面。并且还可以接收一个 data 参数,data 可以传递任意类型、任意大小的数据。

    Router.toDetail({ params: { id: 1 } })
    Router.toDetail({ params: { id: 1, name: 'lbl' } })
    
    Router.toDetail({ data: { name: 'taro', role: [1, 2, 3] } })
    Router.toDetail({ data: 123 })
    Router.toDetail({ data: true })
    

    并且可以通过页面下的 route.config.ts 导出 paramsdata 的类型定义,这样生成的 Router.to** 相关方法会带有类型提示

    // 导出 params 的类型,名字必须是 Params
    export type Params = {
      id: number
      name: string
    }
    
    // 导出 data 的类型,名字必须是 Data
    export type Data = {
      name: string
      role: number[]
    }
    

    3. 路由方法是异步的,页面通过 EventCannal 通信,事件的回调方法可读性差、耦合度高、只能在回调内部处理异常

    tarojs-router-next 的路由跳转会返回一个 Promise,通过 async/await 可以写出同步的写法,详细参考 同步的路由方法

    // page/edit/index
    try {
      // 跳转页面选择城市
      const cityData = await Router.toSelectCity()
      if( !cityData ) return
      // 赋值给表单项
      this.form.city = cityData
    } catch ( err ) {
      console.log( err.message )
    }
    
    // page/select-city/index
    Router.back() // 返回上一个页面,此时上一个页面拿到的是 null
    Router.back( { id: 1, name: '深圳' } ) // 返回上一个页面并返回城市数据
    Router.back( new Error('用户取消选择') ) // 返回上一个页面并抛出异常
    

    4. 路由跳转的鉴权等实现起来比较麻烦

    自己实现路由的鉴权是比较麻烦的事情,而 tarojs-router-next 提供与 koa 使用一致的路由中间件功能,详细参考 路由中间件

    注册一个路由中间件:

    import Taro from '@tarojs/taro'
    import { Middleware, registerMiddleware } from 'tarojs-router-next'
    
    export const M1: Middleware = async (ctx, next) => {
      console.log('中间件执行:', ctx.route.url)
      await next()
      console.log('中间件执行结束')
    }
    
    registerMiddleware(M1)
    

    注册多个路由中间件:

    registerMiddlewares([M1, M2])
    

    有的时候我们希望某个中间件只为特定的页面工作,这个需求可以在中间件中增加判断条件来实现,但在中间件中做这些判断会使中间件的职能不够专一,并且这些判断逻辑无法在多个中间件中复用

    怎么解决呢,我们可以在注册中间件时传递一个方法,将本来要写到中间件中的判断逻辑抽取到该方法中。在路由进入时该方法会被调用并传入当前路由的上下文,若方法返回 true 则为当前路由执行这些中间件

    // 仅为 me 和 home 页面注册该路由中间件
    registerMiddleware(Logger, (ctx) => {
      return ['/pages/me/index', '/pages/home/index'].indexOf(ctx.route.url) !== -1
    })
    
    // 注册多个中间件
    registerMiddlewares([Logger, Auth], (ctx) => {
      return ['/pages/me/index', '/pages/home/index'].indexOf(ctx.route.url) !== -1
    })
    

    一个检查用户是否登录的中间件示例:

    import Taro from '@tarojs/taro'
    import { Middleware, Router } from 'tarojs-router-next'
    
    export const AuthCheck: Middleware<{ mustLogin: boolean }> = async (ctx, next) => {
      if (ctx.route.ext?.mustLogin) {
        const token = Taro.getStorageSync('token')
        if (!token) {
          const { confirm } = await Taro.showModal({
            title: '提示',
            content: '请先登录',
          })
    
          if (confirm) Router.toLogin()
    
          // 打断路由执行
          throw Error('该页面必须要登陆:' + ctx.route.url)
        }
      }
    
      await next()
    }
    

    最后

    完整的代码示例:React 示例Vue3 示例

    详细的文档:查看文档

    7 条回复    2021-04-23 10:15:09 +08:00
    JenJieJu
        1
    JenJieJu  
       2021-04-22 15:37:35 +08:00
    不错不错,有几个问题:
    1. 可以管理路由栈吗
    2. 可以无限层次进入页面吗
    lblblong
        2
    lblblong  
    OP
       2021-04-22 16:05:23 +08:00
    @JenJieJu 我看了一些突破页面栈限制的方案,都有一个最大的问题就是:小程序的页面过渡动画无法自定义,所以体验上会出现用户返回上一个页面,页面却是从右边进来的..,所以我还是建议尽量从业务和交互的设计上避免超出限制

    管理路由是需要哪些支持呢
    weimo383
        3
    weimo383  
       2021-04-22 19:32:40 +08:00 via Android
    讲道理小程序为什么需要路由呢?状态管理存一下状态就行了呗。。。
    lblblong
        4
    lblblong  
    OP
       2021-04-23 09:30:09 +08:00
    @weimo383 这...是 GUI 程序都会要路由系统的吧
    weimo383
        5
    weimo383  
       2021-04-23 10:06:13 +08:00 via Android
    @lblblong 我还是不理解,浏览器地址栏在小程序中又没有。。。
    lblblong
        6
    lblblong  
    OP
       2021-04-23 10:14:26 +08:00
    @weimo383 小程序的每个页面都有地址的,比如你在小程序里分享了一篇文章给好友,分享出去的内容其实是:小程序 appid + 文章页面路径 /pages/article/index?id=123
    lblblong
        7
    lblblong  
    OP
       2021-04-23 10:15:09 +08:00
    @weimo383 好友点击你的分享的时候,就会直接进入文章详情页
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4504 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 02:32 · PVG 10:32 · LAX 19:32 · JFK 22:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.