包详细信息

universal-router

kriasoft63.4kMIT10.0.0

Isomorphic router for JavaScript web applications

isomorphic, universal, router, routing

自述文件

Universal Router

NPM version NPM downloads Library Size Online Chat

Visit Universal Router Website

A simple middleware-style router that can be used in both client-side and server-side applications.

Visit Quickstart Guide (slides)  |  Join #universal-router on Gitter to stay up to date

Features

What users say about Universal Router

Just switched a project over to universal-router. Love that the whole thing is a few hundred lines of flexible, easy-to-read code.

-- Tweet by Greg Hurrell from Facebook

It does a great job at trying to be universal — it's not tied to any framework, it can be run on both server and client, and it's not even tied to history. It's a great library which does one thing: routing.

-- Comment on Reddit by @everdimension

Installation

Using npm:

npm install universal-router --save

How does it look like?

import UniversalRouter from 'https://esm.sh/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>
})

Play with an example on JSFiddle, CodePen, JS Bin in your browser or try RunKit node.js playground.

Documentation

Books and Tutorials

Browser Support

We support all ES5-compliant browsers, including Internet Explorer 9 and above, but depending on your target browsers you may need to include polyfills>) for Map, Promise and Object.assign before any other code.

For compatibility with older browsers you may also need to include polyfills for Array.isArray and Object.create.

Contributing

Anyone and everyone is welcome to contribute to this project. The best way to start is by checking our open issues, submit a bug report or feature request, participate in discussions, upvote or downvote the issues you like or dislike, send pull requests.

Support

Related Projects

  • React Starter Kit — Boilerplate and tooling for building isomorphic web apps with React and Relay.
  • Node.js API Starter Kit — Boilerplate and tooling for building data APIs with Docker, Node.js and GraphQL.
  • ASP.NET Core Starter Kit — Cross-platform single-page application boilerplate (ASP.NET Core, React, Redux).
  • Babel Starter Kit — Boilerplate for authoring JavaScript/React.js libraries.
  • React App SDK — Create React apps with just a single dev dependency and zero configuration.
  • React Static Boilerplate — Single-page application (SPA) starter kit (React, Redux, Webpack, Firebase).
  • History — HTML5 History API wrapper library that handle navigation in single-page apps.
  • Redux-First Routing — A minimal, framework-agnostic API for accomplishing Redux-first routing.

Sponsors

Become a sponsor and get your logo on our README on Github with a link to your site. [Become a sponsor]

Backers

Support us with a monthly donation and help us continue our activities. [Become a backer]

License

Copyright © 2015-present Kriasoft. This source code is licensed under the MIT license found in the LICENSE.txt file. The documentation to the project is licensed under the CC BY-SA 4.0 license.


Made with ♥ by Konstantin Tarkus (@koistya, blog), Vladimir Kutepov and contributors

更新日志

Universal Router Change Log

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

10.0.0 - 2025-05-05

  • Updated path-to-regexp from v6.2.0 to v8.2.0. See changelog (BREAKING CHANGE #218)
  • Since Path To RegExp does not provide an ESM version, it is now bundled into the Universal Router package:
    - const pathToRegexp = require('path-to-regexp')
    + import * as pathToRegexp from 'universal-router/path-to-regexp'
    
  • The import path for generating URLs has changed:
    - import generateUrls from 'universal-router/generateUrls'
    + import generateUrls from 'universal-router/generate-urls'
    

9.2.1 - 2024-11-22

  • Enable noPropertyAccessFromIndexSignature and noUncheckedIndexedAccess checks (#216)

9.2.0 - 2023-06-23

  • Bump TypeScript to 4.9.5 and fix types (#215)

9.1.0 - 2021-06-23

  • Add uniqueRouteNameSep option to generateUrls(router, options) to allow non-unique route names among different branches of nested routes and access them by uniquely generated name (#194)

9.0.1 - 2020-03-11

  • Fix typings: router.resolve() and context.next() always return a promise now (#187)

9.0.0 - 2020-02-27

8.3.0 - 2019-09-17

  • Make generateUrls compatible with UniversalRouterSync (#172)

8.2.1 - 2019-07-20

  • Fix context.next() to throw Route not found instead of TypeError (#169)

8.2.0 - 2019-05-10

  • Improve TypeScript typings (#167)

8.1.0 - 2019-02-20

8.0.0 - 2019-01-15

7.0.0 - 2018-10-11

  • The router no longer mutate errors to avoid issues with non-extensible objects. (BREAKING CHANGE #158).

Migration from v6 to v7:

  • If your code relies on error.context or error.code you still can access them using errorHandler option:
    errorHandler(error, context) {
      const code = error.status || 500
      console.log(error, context, code)
    }
    

6.0.0 - 2018-02-06

  • No special configuration is required for your bundler anymore (say hi to parcel.js).
  • Add an option for global error handling (#147).

Migration from v5 to v6:

  • Use error.code instead of error.status or error.statusCode for error handling.

5.1.0 - 2018-01-16

  • Allow any string to be a valid route name (#145)

5.0.0 - 2017-10-30

  • Skip nested routes when a middleware route returns null (BREAKING CHANGE #140)

Migration from v4 to v5:

  • If you are using resolveRoute option for custom route handling logic then you need to return undefined instead of null in cases when a route should not match
  • Make sure that your middleware routes which return null are working as you expect, child routes are no longer executed in this case

4.3.0 - 2017-10-22

4.2.1 - 2017-10-06

  • Fix order of context.keys when they preserved from parent routes (i.e. keys order is the same as they appear in a url) (#129)

4.2.0 - 2017-09-20

  • Correctly handle trailing slashes in paths of routes (#124)
    If you are using trailing slashes in your paths, then the router will match urls only with trailing slashes:
    const routes = [
      { path: '/posts', ... },  // matches both "/posts" and "/posts/"
      { path: '/posts/', ... }, // matches only "/posts/"
    ]
    
  • Generate url from first path for routes with an array of paths (#124)
    const router = new UniversalRouter({
      name: 'page',
      path: ['/one', '/two', /RegExp/], // only first path is used for url generation
    })
    const url = generateUrls(router)
    url('page') // => /one
    

4.1.0 - 2017-09-20

  • Support for using the same param name in array of paths (#122)

    const router = new UniversalRouter({
      path: ['/one/:parameter', '/two/:parameter'],
      action: (context) => context.params,
    })
    
    router.resolve('/one/a') // => { parameter: 'a' }
    router.resolve('/two/b') // => { parameter: 'b' }
    

4.0.0 - 2017-09-15

  • Rename router.resolve({ path }) to router.resolve({ pathname }) (BREAKING CHANGE #114)
  • Rename context.url to context.pathname (BREAKING CHANGE #114)
  • Remove pretty option from generateUrls(router, options) function in favor of new encode option (BREAKING CHANGE #111)
  • Update path-to-regexp to v2.0.0, see changelog (BREAKING CHANGE #111)
    • Explicitly handle trailing delimiters (e.g. /test/ is now treated as /test/ instead of /test when matching)
    • No wildcard asterisk (*) - use parameters instead ((.*))
  • Add support for repeat parameters (#116)
  • Add encode option to generateUrls(router, options) function for pretty encoding (e.g. pass your own implementation) (#111)
  • Preserve context.keys values from the parent route (#111)
  • Inherit context.params and queryParams from Object (e.g. params.hasOwnProperty() won't throw an exception anymore) (#111)
  • Include the source code of the router in the npm package (#110)

Migration from v3 to v4:

  • Change router.resolve({ path, ... }) to router.resolve({ pathname, ... })
  • Remove trailing slashes from all paths of your routes, i.e.
    • path: '/posts/:uri/' => path: '/posts/:uri'
    • path: '/posts/' => path: '/posts'
    • path: '/' => path: ''
    • etc.
  • Replace path: '*' with path: '(.*)' if any
  • If you are using webpack, change rule (loader) for .js files to include .mjs extension, i.e.
    • test: /\.js$/ => test: /\.m?js$/

3.2.0 - 2017-05-10

  • Add stringifyQueryParams option to generateUrls(router, options) to generate URL with query string from unknown route params (#93)

3.1.0 - 2017-04-20

  • Fix context.next() for multiple nested routes (#91)
  • Add pretty option for generateUrls(router, options) to prettier encoding of URI path segments (#88)
  • Add source maps for minified builds (#87)
  • Include UMD builds to the git repository

3.0.0 - 2017-03-25

  • Update Router API (BREAKING CHANGE)

    import Router from 'universal-router'
    const router = new Router(routes, options)
    router.resolve({ path, ...context }) // => Promise<any>
    
    // previously
    import { resolve } from 'universal-router'
    resolve(routes, { path, ...context }) // => Promise<any>
    

    See #83 for more info and examples

  • context.next() now iterates only child routes by default (BREAKING CHANGE)
    use context.next(true) to iterate through the all remaining routes

  • Remove babel-runtime dependency to decrease library size (BREAKING CHANGE)
    Now you need to care about these polyfills yourself:
  • Add support for URL Generation
    import generateUrls from 'universal-router/generate-urls'
    const url = generateUrls(router)
    url(routeName, params) // => String
    
  • Add support for Dynamic Breadcrumbs, use context.route.parent to iterate
  • Add support for Declarative Routes, new Router(routes, { resolveRoute: customResolveRouteFn })
  • Add support for Base URL option, new Router(routes, { baseUrl: '/base' })
  • Add ability to specify custom context properties once, new Router(routes, { context: { ... } })
  • Rewrite matchRoute function without usage of generators to decrease amount of necessary polyfills
  • Remove usage of String.prototype.startsWith()
  • Add context.url with the original url passed to resolve method
  • Add context property to Route not found error

2.0.0 - 2016-10-20

  • Preserve context.params values from the parent route (#57)
  • Throws an error if no route found (#62)
  • Remove obsolete context.end() method (#60)
  • Remove obsolete match alias for resolve function (#59)
  • Do not throw an error for malformed URI params (#54)
  • Handle null the same way as undefined (#51)
  • Return null instead of undefined to signal no match (#51)
  • Support context.next() across multiple routes (#49)
  • Sequential execution of asynchronous routes (#49)
  • Remove errors handler from core (#48)
  • Drop support of node.js v5 and below (#47)

1.2.2 - 2016-05-31

  • Update UMD build to include missing dependencies (#33)

1.2.1 - 2016-05-12

  • Rename match() to resolve(). E.g. import { resovle } from 'universal-router'
  • Fix an issue when the router throws an exception when the top-level route doesn't have children property
  • Include CommonJS, Harmony Modules, ES5.1 and UMD builds into NPM package
  • Include source maps into NPM package

1.1.0-beta.4 - 2016-04-27

  • Fix optional parameters, e.g. /products/:id? (#27)

1.1.0-beta.3 - 2016-04-08

  • Fix matchRoute() yielding the same route twice when it matches to both full and base URLs

1.1.0-beta.2 - 2016-04-08

  • match(routes, { path, ...context) now throws an error if a matching route was not found (BREAKING CHANGE)
  • If there is a top-level route with path equal to /error, it will be used for error handling by convention

1.1.0-beta.1 - 2016-04-05

  • Remove Router class and router.dispatch() method in favor of match(routes, { path, ...context }), where routes is just a plain JavaScript objects containing the list of routes (BREAKING CHANGE)
  • Add context.end() method to be used from inside route actions
  • Update documentation and code samples

1.0.0-beta.1 - 2016-03-25

  • Rename react-routing to universal-router (BREAKING CHANGE)
  • Remove router.on(path, ...actions) in favor of router.route(path, ...actions) (BREAKING CHANGE)
  • Remove new Router(on => { ... }) initialization option in favor of new Router(routes) (BREAKING CHANGE)
  • Fix ESLint warnings
  • Update unit tests
  • Remove build tools related to project's homepage in favor of Easystatic
  • Refactor project's homepage layout. See docs/assets.
  • Clean up package.json, update Babel and its plug-ins to the latest versions
  • Make the library use babel-runtime package instead of an inline runtime
  • Add CHANGELOG.md file with the notable changes to this project

0.0.7 - 2015-12-13

  • Small bug fixes and improvements