前端微服务之路由篇-Universal Router

npm官网 https://www.npmjs.com/package/universal-router

Universal Router API

Using npm:

npm install universal-router --save

Or using a CDN like unpkg.com or jsDelivr with the following script tag:

<script src="https://unpkg.com/universal-router/universal-router.min.js"></script>

 实例

import UniversalRouter from 'universal-router'
 
const routes = [
  {
    path: '', // optional
    action: () => `<h1>Home</h1>`,
  },
  {
    path: '/posts',
    action: () => console.log('checking child routes for /posts'),
    children: [
      {
        path: '', // optional, matches both "/posts" and "/posts/"
        action: () => `<h1>Posts</h1>`,
      },
      {
        path: '/:id',
        action: (context) => `<h1>Post #${context.params.id}</h1>`,
      },
    ],
  },
]
 
const router = new UniversalRouter(routes)
 
router.resolve('/posts').then(html => {
  document.body.innerHTML = html // renders: <h1>Posts</h1>
})

const router = new UniversalRouter(routes, options)

1.创建一个通用路由器实例,该实例具有单个router.resolve() 方法。

UniversalRouter构造函数期望第一个routes参数的纯javascript对象具有任意数量的参数,其中只需要路径,或者这些对象的数组。第二个选项参数是可选的,您可以通过以下选项:

1.context    -----您想传递给resolveRoute函数任何数据的对象。

2.baseUrl  ------应用程序的基本URL。默认情况下是空字符串“”。公用的URL

如果应用程序中的所有URL都与其他“基础”URL相对应,请使用此选项。

3.resolveRoute  -----用于任何自定义路由处理逻辑的函数

例如,您可以定义此选项以声明的方式处理路由。

默认情况下,路由器调用匹配路由的动作方法。

4.errorHandler ---全局错误处理功能。每次未找到路由或引发错误时,调用错误和上下文参数。

import UniversalRouter from 'universal-router'

const routes = {
  path: '/page',            // string or regexp or array of them, optional

  name: 'page',             // unique string, optional
  parent: null,             // route object or null, automatically filled by the router
  children: [],             // array of route objects or null, optional

  action(context, params) { // function, optional

    // action method should return anything except `null` or `undefined` to be resolved by router
    // otherwise router will throw `Page not found` error if all matched routes returned nothing
    return '<h1>The Page</h1>'
  },
  // ...
}

const options = {
  context: { user: null },
  baseUrl: '/base',
  resolveRoute(context, params) {
    if (typeof context.route.action === 'function') {
      return context.route.action(context, params)
    }
    return undefined
  },
  errorHandler(error, context) {
    console.error(error)
    console.info(context)
    return error.status === 404
      ? '<h1>Page Not Found</h1>'
      : '<h1>Oops! Something went wrong</h1>'
  }
}

const router = new UniversalRouter(routes, options)

router.resolve({ pathname, ...context }) ⇒ Promise<any>

按照定义路由的顺序遍历路由列表,直到找到与所提供的URL路径字符串匹配的第一路由,并且其操作函数返回除空或未定义之外的任何内容。

const router = new UniversalRouter([
  {
    path: '/one',
    action: () => 'Page One',
  },
  {
    path: '/two',
    action: () => `Page Two`,
  },
])

router.resolve({ pathname: '/one' })
  .then(result => console.log(result))
  // => Page One

其中action 只是一个正则函数,它可以或可能不返回任何任意数据——字符串、反作用组件、任何东西!

嵌套路由

const router = new UniversalRouter({
  path: '/admin',
  children: [
    {
      path: '',                        // www.example.com/admin
      action: () => 'Admin Page',
    },
    {
      path: '/users',
      children: [
        {
          path: '',                    // www.example.com/admin/users
          action: () => 'User List',
        },
        {
          path: '/:username',          // www.example.com/admin/users/john
          action: () => 'User Profile',
        },
      ],
    },
  ],
})

router.resolve({ pathname: '/admin/users/john' })
  .then(result => console.log(result))
  // => User Profile

URL参数

捕获命名的路由参数并添加到context.params.中。

const router = new UniversalRouter({
  path: '/hello/:username',
  action: (context) => `Welcome, ${context.params.username}!`,
})

router.resolve({ pathname: '/hello/john' })
  .then(result => console.log(result))
  // => Welcome, john!

或者,捕获的参数可以通过第二个参数传入

const router = new UniversalRouter({
  path: '/hello/:username',
  action: (ctx, { username }) => `Welcome, ${username}!`,
})

router.resolve({ pathname: '/hello/john' })
  .then(result => console.log(result))
  // => Welcome, john!

路由器保留父路由器的 context.params值。如果父和子有冲突的PARAM名称,则子值优先。

这个功能由path-to-regexp npm模块提供动力,并且与许多流行的JavaScript框架(如Express和Koa)中的路由解决方案以相同的方式工作。也检查在线路由器测试器。

上下文

除了URL路径字符串外,任何任意数据都可以传递给router.resolve()方法,该方法在动作函数内部变得可用。

const router = new UniversalRouter({
  path: '/hello',
  action(context) {
    return `Welcome, ${context.user}!`
  },
})

router.resolve({ pathname: '/hello', user: 'admin' })
  .then(result => console.log(result))
  // => Welcome, admin!

outer supports context option in the UniversalRouter 

异步路由

路由在异步功能中发挥了巨大的作用!

const router = new UniversalRouter({
  path: '/hello/:username',
  async action({ params }) {
    const resp = await fetch(`/api/users/${params.username}`)
    const user = await resp.json()
    if (user) return `Welcome, ${user.displayName}!`
  },
})

router.resolve({ pathname: '/hello/john' })
  .then(result => console.log(result))
  // => Welcome, John Brown!

使用 Babel来转义你的代码,, with async / await to normal JavaScript

const route = {
  path: '/hello/:username',
  action({ params }) {
    return fetch(`/api/users/${params.username}`)
      .then(resp => resp.json())
      .then(user => user && `Welcome, ${user.displayName}!`)
  },
}

中间件

任何路由的action函数都可以使用中间件通过调用context.next().

const router = new UniversalRouter({
  path: '', // optional
  async action({ next }) {
    console.log('middleware: start')
    const child = await next()
    console.log('middleware: end')
    return child
  },
  children: [
    {
      path: '/hello',
      action() {
        console.log('route: return a result')
        return 'Hello, world!'
      },
    },
  ],
})

router.resolve({ pathname: '/hello' })
// Prints:
//   middleware: start
//   route: return a result
//   middleware: end

context.next() 只遍历子路由,使用context.next(true),遍历所有剩余的路由

注意,如果中间件动作返回NULL,则路由器将跳过所有嵌套路由,并转到下一个兄弟路由。但是如果动作丢失或返回未定义,路由器将尝试匹配子路径。这可以用于权限检查。

const middlewareRoute = {
  path: '/admin',
  action(context) {
    if (!context.user) {
      return null // route does not match (skip all /admin* routes)
    }
    if (context.user.role !== 'Admin') {
      return 'Access denied!' // return a page (for any /admin* urls)
    }
    return undefined // or `return context.next()` - try to match child routes
  },
  children: [/* admin routes here */],
}

URL生成

在大多数Web应用程序中,只使用超链接的字符串就简单多了。

`<a href="/page">Page</a>`
`<a href="/user/${username}">Profile</a>`
`<a href="/search?q=${query}">Search</a>`
`<a href="/faq#question">Question</a>`
// etc.

然而,对于某些类型的Web应用程序,基于路由名称动态生成URL可能是有用的。这就是为什么这个特性可以用作带有简单API generateUrls(router, options) ⇒ Function 其中返回的函数用于生成urls url(routeName, params) ⇒ String.

import UniversalRouter from 'universal-router'
import generateUrls from 'universal-router/generateUrls'

const routes = [
  { name: 'users', path: '/users' },
  { name: 'user', path: '/user/:username' },
]

const router = new UniversalRouter(routes, { baseUrl: '/base' })
const url = generateUrls(router)

url('users')                          // => '/base/users'
url('user', { username: 'john' })     // => '/base/user/john'

这种方法对于运行时动态添加的路由也很有效。

routes.children.push({ path: '/world', name: 'hello' })

url('hello')                          // => '/base/world'

使用encode 选项对URI路径段进行自定义编码。默认情况下使用 encodeURIComponent

const prettyUrl = generateUrls(router, { encode: (value, token) => value })

url('user', { username: ':/' })       // => '/base/user/%3A%2F'
prettyUrl('user', { username: ':/' }) // => '/base/user/:/'

为stringifyQueryParams选项提供一个函数,用未知路径PARAM生成查询字符串URL。

const urlWithQueryString = generateUrls(router, {
  stringifyQueryParams(params) {
    return Object.keys(params).map(key => `${key}=${params[key]}`).join('&')
  },
})

const params = { username: 'John', busy: 1 }
url('user', params)                   // => /base/user/John
urlWithQueryString('user', params)    // => /base/user/John?busy=1

或使用外部库,如QS、查询字符串等。

import qs from 'qs'
generateUrls(router, { stringifyQueryParams: qs.stringify })

路由重定向

重定向

向路由添加重定向的最简单方法是从动作方法中返回可以在路由解析结束时解释为重定向的内容,例如:

import UniversalRouter from 'universal-router'

const router = new UniversalRouter([
  {
    path: '/redirect',
    action() {
      return { redirect: '/target' } // <== request a redirect
    },
  },
  {
    path: '/target',
    action() {
      return { content: '<h1>Content</h1>' }
    },
  },
])

router.resolve('/redirect').then(page => {
  if (page.redirect) {
    window.location = page.redirect // <== actual redirect here
  } else {
    document.body.innerHTML = page.content
  }
})

具体的参照https://github.com/kriasoft/universal-router/blob/master/docs/redirects.md

猜你喜欢

转载自blog.csdn.net/qq_31965515/article/details/83416816