uni-app 系列(二) 路由封装

2,576 阅读3分钟

简洁的路由

之前也写过一篇关于路由的封装,但是没有拦截器

uni-app 插件市场也有很多封装好的路由,也有几个非常好的,自己为什么不用了,因为封装过渡了(代码的大小几十kb到一百多kb,要是可以插件化,选择自己的功能就完美了),要知道小程序是有限制包大小的,就算没有,我们也应该尽量小,而且有很多功能我是使用不到的

现在简化以前的路由,下面的代码只是在自己的项目下使用没有什么问题,大家参考修改适合自己的项目就可以了

// 这是基本结构
// 把 uni 路由 promise 化
class Router {
  push ({ type = 'navigateTo', url }) {
    const promise = new Promise((resolve, reject) => {
      uni[type]({
        url: url,
        success: resolve,
        fail: reject
      })
    })
    return promise
  }

  back (delta = 1) {
    uni.navigateBack({ delta })
  }
}

怎么处理带参数的路由

// 先看看使用方式
const router = new Router()
router.push({
  path: '/pages/index/index',
  query: { id: 1 }
})

// 获取拼接好的 url
const getUrl = (path, query) => {
  let url = path

  if (Object.prototype.toString.call(query) === '[object Object]') {
    url = `${path}?query=${JSON.stringify(query)}`
  }

  return url
}

添加拦截器

只提供全局的 beforeEachafterEach 拦截器

// 先看使用方式
const router = new Router()
router.beforeEach((to, from, next) => {
  console.log('beforeEach', to, from)
  if (to.path === '/pages/test/index') {
    next({
      path: '/pages/login/index'
    })
    return
  }
  next()
})

router.afterEach((to, from) => {
  console.log('afterEach', to, from)
})

// to form 参数包含两个参数 path: 跳转的路径 query: 路由所带参数
// 下面创建一个获取 to form 的方法
const getToAndFrom = ({ path, query }) => {
  let from = {}
  // getCurrentPages 这个是原生函数用于获取当前页面栈的实例
  const routers = getCurrentPages()
  if (routers.length > 0) {
    // 获取最后一个元素
    const router = routers[routers.length - 1]
    // 这下面的逻辑结合上面的 getUrl 方法
    const { query, ...options } = router.options
    let args = options
    if (query) {
      args = {
        ...args,
        ...JSON.parse(query)
      }
    }
    
    from = {
      path: router.route,
      query: args
    }
  }

  const to = {
    path,
    query: query || {}
  }

  return {
    to,
    from
  }
}

// 创建一个 next 方法
// 用于 beforeEach 拦截
_next ({ to, from }) {
  let i = 0

  return new Promise((resolve, reject) => {
    const _next = (args) => {
      if (Object.prototype.toString.call(args) === '[object Object]') {
        this.push(args)
        return reject('拦截并跳转')
      }

      if (args === false) {
        return reject('拦截终止跳转')
      }

      const task = this._beforeEach[i++]
      if (!task) {
        return resolve()
      }
      // 重要的是这里
      return new Promise((next) => {
        task(to, from, next)
      }).then(_next).catch(reject)
    }

    _next()
  })
}

push (params) {
  const { type, path, query } = params
  // getToAndFromAndUrl 这个方法是合并上面的 getToAndFrom 和 getUrl 方法
  const { to, from, url } = getToAndFromAndUrl({ path, query })
  
  // 这里调用 beforeEach 拦截器
  return this._next({ to, from }).then(() => {
    // _push 就是上面的 push
    return this._push({ type, url, to, from })
  }).catch(err => {
    console.log(err)
  })
}

完整代码

// 获取拼接好的 url、to、from
const getToAndFromAndUrl = ({ path, query }) => {
  let url = path
  let from = {}

  const routers = getCurrentPages()
  if (routers.length > 0) {
    const router = routers[routers.length - 1]
    const { query, ...options } = router.options
    let args = options
    if (query) {
      args = {
        ...args,
        ...JSON.parse(query)
      }
    }
    
    from = {
      path: router.route,
      query: args
    }
  }

  if (Object.prototype.toString.call(query) === '[object Object]') {
    url = `${path}?query=${JSON.stringify(query)}`
  }

  const to = {
    path,
    query: query || {}
  }

  return {
    to,
    from,
    url
  }
}

export default class Router {
  _beforeEach = []
  _afterEach = []

  _push ({ type = 'navigateTo', url, to, from }) {
    const promise = new Promise((resolve, reject) => {
      uni[type]({
        url: url,
        success: resolve,
        fail: reject
      })
    })

    promise.then(() => {
      // 跳转成功后了调用 afterEach 拦截器
      this._afterEach.forEach(fn => {
        fn(to, from)
      })
    }).catch(err => {
      console.log(err)
    })

    return promise
  }

  push (params) {
    const { type, path, query } = params
    const { to, from, url } = getToAndFromAndUrl({ path, query })

    return this._next({ to, from }).then(() => {
      return this._push({ type, url, to, from })
    }).catch(err => {
      console.log(err)
    })
  }

  _next ({ to, from }) {
    let i = 0

    return new Promise((resolve, reject) => {
      const _next = (args) => {
        if (Object.prototype.toString.call(args) === '[object Object]') {
          this.push(args)
          return reject('拦截并跳转')
        }

        if (args === false) {
          return reject('拦截终止跳转')
        }

        const task = this._beforeEach[i++]
        if (!task) {
          return resolve()
        }

        return new Promise((next) => {
          task(to, from, next)
        }).then(_next).catch(reject)
      }

      _next()
    })
  }

  beforeEach (fn) {
    this._beforeEach.push(fn)
  }

  afterEach (fn) {
    this._afterEach.push(fn)
  }

  back (delta = 1) {
    uni.navigateBack({ delta })
  }
}

参数获取

function parseURL () {
  const routers = getCurrentPages()
  if (routers.length > 0) {
    const router = routers[routers.length - 1]
    const { query, ...options } = router.options
    let args = options
    if (query) {
      args = {
        ...args,
        ...JSON.parse(query)
      }
    }
    return args
  }
  return {}
}