Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
101 lines (68 sloc) 5.35 KB
title lunr draft date author metaTags tags image
Router 5 a new framework-agnostic HTML5 router
2015-07-10 17:00:00 -0700
Thomas Roch

Router5: a new HTML5 router

I imagine a lot of developers who will see for the first time router5 will ask themselves the following question: is it yet another router? is it any good? Why oh why do people keep writing new routers all the time?

It is not always easy to see the potential of something straight away, or understand the motivations behind. I therefore decided to try to tell you more about router5, why I decided to develop an entire new routing solution, and what problems it tries to solve.

Being reactive

Observable patterns and functional reactive programming are becoming more and more popular. So I started to use React. Before React, I have heavily used Angular. I still do, and I'll probably continue to do so with Angular2. Coming from Angular, I started to look for a routing solution in React and used the most popular one: react-router. React-router is a great tool, and it is easy to use: like in ui-router or ngRoute, I can simply tell it what component / view to render for each route.

I became increasingly keen on using more observables and observers in my application, testing Flux patterns. Very soon, I found my application disjointed: using nice observable patterns for data with sideways data loading, but giving away control on routing.

I realised that in order to treat route changes like data changes, I would need a router which would favour convention over configuration.

Existing routers are black boxes

The most popular routers those days are tied to frameworks (or libraries) and are fairly large pieces of software, tightly coupled together. Angular2 and Aurelia, for example, include their own routing solution with exciting new functionalities: activation / deactivation, use of pipelines, etc...

Routers in Angular1, Angular2, Aurelia, Ember, React... have all something in common: they implement everything from path recognition to navigation and history management. They all share similar concepts or technical hurdles, but share very little code or conventions.

Making the same mistakes?

I came across routington thanks to the late DailyJS. It is a trie-based URL router: it organises URLs in a tree. The concept is great, and I thought it could be enhanced by organising named routes in a tree rather than URL segments. That way, every branch of a named routes tree is a valid route. From an application point of view, it also means it is more maintainable: you reference routes by name rather than URL.

Using a tree makes building and matching paths easy. It also goes hand in hand with a component tree. If one can do sideways data loading, why not do sideways route loading, or compose the two?

From routington came route-node. I then needed to use a URL parsing library. I looked at route-parser, url-pattern and path-to-regexp. Having routes in a tree, I needed a library which could match URLs fully or partially (going down the tree until a match is found), I also needed to generate URLs by passing parameters.

None of the libraries mentioned could do all of that, so I wrote path-parser.


router5: framework-agnostic and reactive

router5 is built on tope of route-node and path-parser. It delegates route management and route matching / building and focus on navigation, history and triggering listeners. Its strength comes from the three different types of listeners it can register:

  • Listeners triggered for every route change
  • Listeners listening to a specific route
  • Listeners being triggered on the lowest common node between the previous and the current route: this listener is aimed at minimizing an application re-render on a route change.

Navigation from 'users.view' to 'orders.completed'

When navigation from users.view to orders.completed:

  • .addListener(fn) will be called
  • .addNodeListener('', fn) will be called
  • .addRouteListener('orders.completed', fn) will be called

Navigation from 'orders.completed' to 'orders.pending'

When navigation from orders.completed to orders.pending:

  • .addListener(fn) will be called
  • .addNodeListener('orders', fn) will be called
  • .addRouteListener('orders.pending', fn) will be called