1. 程式人生 > >前端微服務之路由篇-Universal Router

前端微服務之路由篇-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