在 Vue 和 React 中获得一致的路由开发体验

308 阅读4分钟

oh-router 是一个既支持 Vue 也支持 React 的路由库,它的核心功能与框架解绑,可以在 Vue 和 React 中提供一致的路由开发体验

基本使用示例

React 中使用

安装依赖

$ npm install --save oh-router oh-router-react

下面是一个结合 React 最基本的使用案例:在 StackBlitz 中打开

import { Router } from 'oh-router'
import { RouterView, Link } from 'oh-router-react'
import ReactDOM from 'react-dom/client'

const router = new Router({
  routes: [
    {
      path: '/',
      element: () => (
        <div>
          <div>Home</div>
          <Link to="/about">to About</Link>
        </div>
      ),
    },
    {
      path: '/about',
      element: () => (
        <div>
          <div>About</div>
          <Link to="/">to Home</Link>
        </div>
      ),
    },
  ],
})

ReactDOM.createRoot(document.getElementById('root')!).render(
  <RouterView router={router} />
)

Vue 中使用

安装依赖

$ npm install --save oh-router oh-router-vue

下面是一个结合 Vue 最基本的使用案例:在 StackBlitz 中打开

<div id="app">
  <router-view />
</div>

<script>
  import { Router } from 'oh-router'
  import { installForVue } from 'oh-router-vue'
  import { createApp } from 'vue'

  const router = new Router({
    routes: [
      {
        path: '/',
        element: {
          template: `<div>
          <div>Home</div>
          <router-link to="/about">to About</router-link>
        </div`,
        },
      },
      {
        path: '/about',
        element: {
          template: `<div>
          <div>About</div>
          <router-link to="/">to Home</router-link>
        </div`,
        },
      },
    ],
  })

  const app = createApp({})
  app.use(installForVue(router))
  app.mount('#app')
</script>

一、配置路由

React 中配置路由

import Layout from 'src/layout'
import Login from 'src/pages/login'
import User from 'src/pages/user'
import UserManager from 'src/pages/userManager'
import NotFound from 'src/pages/notFound'

const router = new Router({
  routes: [
    // 常规路由
    {
      path: '/login',
      element: <Login />,
    },
    // 嵌套路由
    {
      path: '/',
      element: <Layout />,
      children:
        // 动态路由
        {
          path: '/user/:id',
          element: <User />
        },
        // 常规路由
        {
          // 由于该路由更加精确,所以优先级比 /user/:id 要高
          // 当访问 /user/manager 时将匹配到该路由
          path: '/user/manager',
          element: <UserManager />
        },
      ]
    },
    // 404 路由
    {
      path: '*',
      element: <NotFound />
    }
  ],
})

Vue 中配置路由

这部分和 React 的唯一区别就是 element 属性的类型

import Layout from 'src/layout.vue'
import Login from 'src/pages/login.vue'
import User from 'src/pages/user.vue'
import UserManager from 'src/pages/userManager.vue'
import NotFound from 'src/pages/notFound.vue'

const router = new Router({
  routes: [
    // 常规路由
    {
      path: '/login',
      element: Login,
    },
    // 嵌套路由
    {
      path: '/',
      element: Layout,
      children:
        // 动态路由
        {
          path: '/user/:id',
          element: User
        },
        // 常规路由
        {
          // 由于该路由更加精确,所以优先级比 /user/:id 要高
          // 当访问 /user/manager 时将匹配到该路由
          path: '/user/manager',
          element: UserManager
        },
      ]
    },
    // 404 路由
    {
      path: '*',
      element: NotFound
    }
  ],
})

二、路由导航

oh-router 提供了三种路由导航方式:

  1. 通过 Router.navigateRouter.back 方法
  2. 通过 useNavigate 获取一个导航方法
  3. 通过 <Link> 组件进行路由导航

React 中路由导航

// 1. Router.navigate,这里的 router 即是上面创建的 Router 对象
router.navigate('/user/manager')
router.navigate('/user/manager', {replace: true})
router.navigate(-2)
router.back()

function User() {
  // 2. useNavigate
  const navigate = useNavigate()
  navigate('/user/manager')
  navigate('/user/manager', {replace: true})
  navigate(-2)

  // 3. <Link>
  return <div>
    <Link to={-1}>Back</Link>
    <Link to="/user/manager">Manager</Link>
    <Link to="/user/manager" replace>Manager</Link>
  <div>
}

Vue 中路由导航

<template>
  <div>
    <!-- 3. <Link> -->
    <router-link to="/user/manager">Manager</router-link>
    <router-link to="/user/manager" replace="true">Manager</router-link>
  <div>
</template>

<script setup>
// 1. Router.navigate,这里的 router 即是上面创建的 Router 对象
router.navigate('/user/manager')
router.navigate('/user/manager', {replace: true})
router.navigate(-2)
router.back()

// 2. useNavigate
const navigate = useNavigate()
navigate('/user/manager')
navigate('/user/manager', {replace: true})
navigate(-2)
</script>

三、获取参数

React 中获取参数

import { useParams } from 'oh-router-react'

export const User = () => {
  // 通过 useParams 获取参数
  const params = useParams()
  return <div>...<div>
}

Vue 中获取参数

setup 中

<script setup>
  import { watch } from 'vue'
  import { useParams } from 'oh-router-vue'

  // 通过 useParams 获取参数
  const params = useParams()

  // 监听参数变化
  watch(params, (newParams) => {
    // do something
  })
</script>

非 setup 中

<script>
  export default {
    created() {
      // 通过 $location.params 获取参数
      this.$location.params

      // 监听参数变化
      this.$watch(
        () => this.$location.params,
        () => {
          // do something
        }
      )
    },
  }
</script>

四、嵌套路由

React 中使用嵌套路由

使用嵌套路由首先需要在配置路由时使用父子结构:

const router = new Router({
  routes: [
    {
      path: '/',
      element: <Layout />,
      // 通过 children 定义父子结构路由
      children:
        {
          path: '/users',
          element: <UserManager />
        },
        {
          path: '/books',
          element: <BookManager />
        },
      ]
    },
  ],
})

最后在父路由组件中使用 <Outlet> 声明子路由出口

import { Outlet } from 'oh-router-react'
export default function Layout(){
  return <div>
    <NavBar/>
    <Outlet />
  <div>
}

这样就完成了,当我们访问 /users 时得到的页面结构:

<div>
  <NavBar />
  <UserManager />
</div>

这样就完成了,当我们访问 /books 时得到的页面结构:

<div>
  <NavBar />
  <BookManager />
</div>

Vue 中使用嵌套路由

和上面完全一样,只是 <Outlet> 注册成了全局组件,不需要导入,而且在 Vue 中使用时名字不一样:

<template>
  <div>
    <nav-bar />
    <!-- 等效于 React 中的 <Outlet /> -->
    <router-outlet />
  </div>
</template>

五、路由中间件

路由中间件和 vue-router 中的路由守卫有点像,主要用来做一些页面权限验证,比如一个用户登陆验证的中间件:

import { Middleware } from 'oh-router'
import { router } from './router'
import store from 'src/store' // 我们假设用户信息在 store 中

export class MustLoginMiddleware extends Middleware {
  handler = async (ctx, next) => {
    if (store.user.hasLogin()) {
      // 已登录则放行
      await next()
    } else {
      // 没登陆前往登陆
      router.navigate('/login')
    }
  }

  // register 返回一个布尔值,为 true 则为当前导航的路由注册该中间件
  register = ({ to }) => {
    // 如果 pathname 不是 '/login' 则为当前路由注册该中间件
    return to.pathname !== '/login'
  }
}

还可以在配置路由时添加一些元数据,以便在中间件中访问,比如通过元数据声明路由允许哪些权限访问:

const router = new Router({
  routes: [
    {
      path: '/user/manager',
      element: <UserManager />
      // 添加元数据
      meta: {
        role: ['superAdmin']
      }
    }
  ]
})

然后在权限验证的中间件中使用

import { Middleware } from 'oh-router'
import { router } from './router'
import store from 'src/store' // 我们假设用户信息在 store 中

export class RoleCheckMiddleware extends Middleware {
  handler = async ({ to }, next) => {
    if (to.meta.role!.indexOf(store.user.role) !== -1) {
      // 用户权限在当前页面允许的权限内则放行
      await next()
    } else {
      // 用户无权访问则弹窗警告并重定向到首页
      alert('无权访问!')
      router.navigate('/')
    }
  }

  register = ({ to }) => {
    // 如果 meta.role 存在的话则为当前路由注册该中间件
    return !!to.meta.role
  }
}

最后记得把这些中间件配置到路由中

const router = new Router({
  routes: [
    {
      path: '/user/manager',
      element: <UserManager />
      // 添加元数据
      meta: {
        role: ['superAdmin']
      }
    }
  ],
  // 配置中间件
  middlewares: [
    new MustLoginMiddleware(),
    new RoleCheckMiddleware()
  ]
})

最后

一些使用示例:

文档 & 仓库

官方文档 | GitHub 仓库