npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

uni-router-enhance

v1.0.5

Published

一个为 uni-app 设计的类型安全路由增强库,提供完整的 TypeScript 支持、路由守卫、动态处理函数等高级特性

Readme

uni-router-enhance

一个为 uni-app 设计的类型安全路由增强库,提供完整的 TypeScript 类型支持、路由守卫、动态处理函数等高级特性。

✨ 特性

  • 🔒 完全类型安全 - 基于 pages.json 自动生成路由类型,避免路由拼写错误
  • 🛡️ 导航守卫 - 支持 beforeEachafterEach 全局守卫
  • 🎯 动态处理函数 - 为特定路由注册数据预加载、权限检查等处理逻辑
  • 📦 查询参数类型化 - 支持 TypeScript 类型推断的查询参数
  • 🔄 自动类型生成 - Vite 插件自动从 pages.json 生成路由类型
  • 🎨 灵活的页面关闭策略 - 支持 navigateToredirectToreLaunch 等多种跳转方式
  • 💾 路由数据缓存 - 自动缓存查询参数和处理函数返回值

📦 安装

npm install uni-router-enhance
# 或
pnpm add uni-router-enhance
# 或
yarn add uni-router-enhance

🚀 快速开始

1. 配置 Vite 插件

vite.config.ts 中配置自动类型生成插件:

import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { routeTypesPlugin } from 'uni-router-enhance'

export default defineConfig({
  plugins: [
    uni(),
    // 自动从 pages.json 生成路由类型
    routeTypesPlugin('./types/auto-page.d.ts')
  ]
})

2. 创建 Router 实例

src/router/index.ts 中创建 router 实例:

import { createRouter } from 'uni-router-enhance'
import pagesJson from '../pages.json'
import type { ENHANCE_ROUTE_PATH } from '../../types/auto-page.d.ts'

// 创建路由实例,传入 pages.json 配置
const router = createRouter<ENHANCE_ROUTE_PATH>(pagesJson)

// 配置全局前置守卫
router.beforeEach((to, from) => {
  console.log('导航前:', to, from)
  // 返回 false 可以取消导航
  // 返回路由名称或路由对象可以重定向
})

// 配置全局后置守卫
router.afterEach((to, from) => {
  console.log('导航后:', to, from)
})

// 导出钩子函数供页面使用
const { useRouter, useRoute } = router

export {
  useRouter,
  useRoute,
  router
}

3. 在页面中使用

基本路由跳转

<script setup lang="ts">
import { useRouter } from '@/router'

const { push } = useRouter()

// 简单跳转(类型安全)
const goToHome = () => {
  push('home')
}

// 带查询参数跳转
const goToDetail = () => {
  push({
    path: 'detail',
    query: {
      id: '123',
      name: 'Product'
    }
  })
}

// 带回调的跳转
const goToProfile = () => {
  push('profile', {
    success: (result) => {
      console.log('跳转成功,handler 返回:', result)
    },
    fail: (error) => {
      console.error('跳转失败:', error)
    }
  })
}
</script>

页面关闭策略

// 默认: navigateTo - 保留当前页面
push({
  path: 'detail'
})

// redirectTo - 关闭当前页面
push({
  path: 'login'
  close: 'current'
})

// reLaunch - 关闭所有页面
push({
  path: 'index',
  close: 'all'
})

获取当前路由信息

<script setup lang="ts">
import { useRoute } from '@/router'

const route = useRoute()

// 访问路由信息
console.log('当前路由名称:', route.name)
console.log('路由元信息:', route.meta)
console.log('查询参数:', route.query)
console.log('Handler 返回值:', route.handlerResult)
</script>

<template>
  <view>
    <text>当前页面: {{ route.name }}</text>
    <text>参数 ID: {{ route.query.id }}</text>
  </view>
</template>

🔧 高级功能

导航守卫

全局前置守卫 (beforeEach)

router.beforeEach((to, from) => {
  console.log(`从 ${from.name} 跳转到 ${to.name}`)
  
  // 权限检查示例
  if (to.meta?.requireAuth && !isLoggedIn()) {
    // 重定向到登录页
    return 'login'
  }
  
  // 返回 false 取消导航
  if (someCondition) {
    return false
  }
  
  // 不返回或返回 true 继续导航
})

全局后置守卫 (afterEach)

router.afterEach((to, from) => {
  // 页面访问统计
  analytics.track('page_view', {
    from: from.name,
    to: to.name,
    timestamp: Date.now()
  })
  
  // 设置页面标题
  if (to.meta?.title) {
    uni.setNavigationBarTitle({ title: to.meta.title })
  }
})

守卫返回值

  • undefinedtrue: 继续导航
  • false: 取消导航
  • 路由名称字符串: 重定向到指定路由
  • 路由对象: 重定向到指定路由并携带参数
router.beforeEach((to, from) => {
  // 简单重定向
  if (needRedirect) {
    return 'home'
  }
  
  // 带参数重定向
  if (needRedirectWithParams) {
    return {
      path: 'detail',
      query: { id: '123' }
    }
  }
})

路由处理函数 (Handler)

Handler 函数允许你在路由跳转时执行自定义逻辑,例如数据预加载、权限验证、埋点上报等。

注册 Handler

import { router } from '@/router'

// 数据预加载示例
router.register('productDetail', async (payload) => {
  const { query } = payload
  const productId = query.id
  
  // 预加载商品数据
  const product = await fetchProduct(productId)
  
  // 返回的数据可在目标页面通过 route.handlerResult 获取
  return product
})

// 权限检查示例
router.register('adminPanel', async (payload) => {
  const user = await getCurrentUser()
  
  if (!user.isAdmin) {
    throw new Error('无权限访问管理面板')
  }
  
  return { allowed: true }
})

// 埋点上报示例
router.register('orderList', async (payload) => {
  analytics.track('page_view', {
    page: 'orderList',
    timestamp: Date.now(),
    ...payload.query
  })
})

获取 Handler 返回值

<script setup lang="ts">
import { useRoute } from '@/router'

const route = useRoute()

// 获取 handler 返回的数据
const product = route.handlerResult as Product

onMounted(() => {
  if (product) {
    console.log('预加载的商品数据:', product)
  }
})
</script>

动态管理 Handler

// 检查是否已注册
if (router.has('productDetail')) {
  console.log('已注册 productDetail handler')
}

// 注销 handler(返回取消函数)
const unregister = router.register('temp', async () => {
  // 临时逻辑
})

// 稍后移除
unregister()

// 或直接移除
router.unregister('temp')

路由元信息 (Meta)

路由元信息从 pages.json 自动提取,包含以下字段:

interface RouteMeta {
  /** 页面路径 */
  url: string
  /** 页面标题 */
  navigationBarTitleText?: string
  /** 是否为 tabBar 页面 */
  isTabBar?: boolean
  /** 页面唯一标识 */
  name: string
  /** 页面样式配置 */
  style?: Record<string, any>
}

pages.json 中配置的页面样式会自动映射到 meta:

{
  "pages": [
    {
      "path": "pages/home/index",
      "style": {
        "navigationBarTitleText": "首页",
        "enablePullDownRefresh": true
      }
    }
  ]
}

访问元信息:

const route = useRoute()
console.log(route.meta?.navigationBarTitleText) // "首页"
console.log(route.meta?.isTabBar) // true/false

📝 API 参考

createRouter

创建 router 实例。

function createRouter<TName extends string>(
  routes?: PagesConfig
): Router<TName>

参数:

  • routes: pages.json 配置对象(可选)

返回: Router 实例

Router 实例方法

register

注册路由处理函数。

register(name: TName, handler: RouteHandler): () => void

参数:

  • name: 路由名称
  • handler: 处理函数,接收 payload,可返回任意值或 Promise

返回: 取消注册的函数

unregister

移除已注册的处理函数。

unregister(name: TName): boolean

返回: 是否成功移除

has

检查是否已注册处理函数。

has(name: TName): boolean

beforeEach

添加全局前置守卫。

beforeEach(guard: NavigationGuard<TName>): () => void

返回: 移除守卫的函数

afterEach

添加全局后置守卫。

afterEach(guard: NavigationGuard<TName>): () => void

返回: 移除守卫的函数

useRouter

返回路由操作钩子。

useRouter(): RouterHookResult<TName>

返回对象:

  • push: 类型安全的路由跳转函数

useRoute

返回当前路由信息。

useRoute(): RouteInfo<TName>

返回对象:

  • name: 当前路由名称
  • meta: 路由元信息
  • query: 查询参数
  • handlerResult: Handler 返回值

useRouter 返回的方法

push

类型安全的路由跳转。

push(
  data: TName | RouterParams<TName>,
  callbacks?: {
    success?: (result?: unknown) => void
    fail?: (error?: any) => void
  }
): Promise<void>

参数:

  • data: 路由名称字符串或路由参数对象
  • callbacks: 可选的成功/失败回调

RouterParams 对象:

interface RouterParams<TPath extends string> {
  path?: TPath              // 路由名称
  query?: Record<string, any>  // 查询参数
  close?: CloseTypes           // 页面关闭策略
  success?: (result?: unknown) => void  // 成功回调
  fail?: (error?: any) => void          // 失败回调
}

CloseTypes 枚举

页面关闭策略。

enum CloseTypes {
  default = 'default',  // navigateTo - 保留当前页面
  current = 'current',  // redirectTo - 关闭当前页面
  all = 'all'           // reLaunch - 关闭所有页面
}

routeTypesPlugin

Vite 插件,自动生成路由类型。

function routeTypesPlugin(dts: string): Plugin

参数:

  • dts: 类型文件输出路径

示例:

routeTypesPlugin('./types/auto-page.d.ts')

生成的类型文件示例:

export type ENHANCE_ROUTE_PATH =
  | 'demo'
  | 'home'
  | 'index'
  | 'profile'

🎯 完整示例

示例 1:电商应用

// router/index.ts
import { createRouter } from 'uni-router-enhance'
import pagesJson from '../pages.json'
import type { ENHANCE_ROUTE_PATH } from '../../types/auto-page.d.ts'

const router = createRouter<ENHANCE_ROUTE_PATH>(pagesJson)

// 全局登录检查
router.beforeEach((to, from) => {
  const needAuth = ['profile', 'order', 'cart'].includes(to.name)
  
  if (needAuth && !isLoggedIn()) {
    uni.showToast({ title: '请先登录', icon: 'none' })
    return 'login'
  }
})

// 商品详情页数据预加载
router.register('productDetail', async (payload) => {
  const { query } = payload
  const product = await fetchProduct(query.id)
  return product
})

// 订单列表页权限检查
router.register('orderList', async () => {
  const user = await getCurrentUser()
  if (!user.hasOrders) {
    throw new Error('暂无订单权限')
  }
})

export const { useRouter, useRoute } = router

示例 2:商品详情页

<script setup lang="ts">
import { useRoute } from '@/router'

// 获取路由信息
const route = useRoute()

// 从 handler 获取预加载的数据
const product = computed(() => route.handlerResult as Product)

onLoad(() => {
  console.log('页面参数:', route.query)
  console.log('预加载数据:', product.value)
})
</script>

<template>
  <view class="product-detail">
    <image :src="product?.image" />
    <text>{{ product?.name }}</text>
    <text>¥{{ product?.price }}</text>
  </view>
</template>

示例 3:商品列表页

<script setup lang="ts">
import { useRouter } from '@/router'

const { push } = useRouter()

const products = ref([])

const goToDetail = (productId: string) => {
  push({
    path: 'productDetail',
    query: { id: productId },
    success: () => {
      console.log('跳转成功')
    },
    fail: (error) => {
      uni.showToast({ title: error.message, icon: 'none' })
    }
  })
}
</script>

<template>
  <view>
    <view 
      v-for="item in products" 
      :key="item.id"
      @click="goToDetail(item.id)"
    >
      {{ item.name }}
    </view>
  </view>
</template>

🔍 TypeScript 支持

本库完全使用 TypeScript 编写,提供完整的类型定义。

自动类型推断

// ✅ 类型安全 - 路由名称会被自动检查
push('home')

// ❌ 类型错误 - 不存在的路由
push('nonexistent')

// ✅ 查询参数类型安全
push({
  path: 'detail',
  query: {
    id: '123',
    tab: 'info'
  }
})

自定义类型

// 定义查询参数类型
interface ProductDetailQuery {
  id: string
  from?: 'list' | 'search'
}

// 在 handler 中使用
router.register('productDetail', async (payload) => {
  const query = payload.query as ProductDetailQuery
  console.log(query.id, query.from)
})

⚠️ 注意事项

  1. TabBar 页面限制

    • TabBar 页面只能使用 uni.switchTab 跳转
    • 跳转到 TabBar 页面时,query 参数会被忽略
  2. 路由名称提取规则

    • 路由名称从页面路径的第二段提取
    • 例如:pages/home/index → 路由名称为 home
    • 分包路径:subpackage/detail/index → 路由名称为 detail
  3. Handler 执行时机

    • Handler 在导航守卫之后、页面跳转之前执行
    • Handler 抛出错误会阻止页面跳转
    • Handler 返回 false 会取消导航
  4. 缓存清理

    • 页面缓存在跳转失败时会自动清理
    • 建议在页面 onLoad 后及时获取缓存数据

🤝 贡献

欢迎提交 Issue 和 Pull Request!

📄 License

MIT License


Made with ❤️ for uni-app developers