├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 James Smith 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # router-comparison 2 | 3 | Wtf even are routers? This repository is an attempt to address that question by comparing the 4 | features of the most popular client side routers. 5 | 6 | ## Table of Contents 7 | 8 | - [About](#about) 9 | - [Why](#why) 10 | - [Routers Compared](#routers-compared) 11 | - [Inspirations](#inspirations) 12 | - [Omissions](#omissions) 13 | - [Features](#features) 14 | - [DSL](#dsl) 15 | - [Regex for `:dynamic`](#regex-for-dynamic) 16 | - [Splats](#splats) 17 | - [Optional segments](#optional-segments) 18 | - [Unnamed segments](#unnamed-segments) 19 | - [Regex instead of DSL](#regex-instead-of-dsl) 20 | - [Asynchronous](#asynchronous) 21 | - [Sort: specificity](#sort-specificity) 22 | - [Sort: deepest](#sort-deepest) 23 | - [Sort: order added](#sort-order-added) 24 | - [Nested routes](#nested-routes) 25 | - [Reset namespace](#reset-namespace) 26 | - [Absolute URLs](#absolute-urls) 27 | - [Optional history](#optional-history) 28 | - [Includes history](#includes-history) 29 | - [Cancel navigation](#cancel-navigation) 30 | - [Redirect navigation](#redirect-navigation) 31 | - [Template links](#template-links) 32 | - [`.active` links](#active-links) 33 | - [Query params](#query-params) 34 | - ['Index' states](#index-states) 35 | - ['Abstract' states](#abstract-states) 36 | - ['Loading' states](#loading-states) 37 | - ['Error' states](#error-states) 38 | - ['Not Found' states](#not-found-states) 39 | - [Pubsub](#pubsub) 40 | - [Not found event](#not-found-event) 41 | - [Scrolling](#scrolling) 42 | - [Group data fetching](#group-data-fetching) 43 | - [Enter hook](#enter-hook) 44 | - [Exit hook](#exit-hook) 45 | - [Update hook](#update-hook) 46 | 47 | ## About 48 | 49 | Routers are playing an increasingly important role in client side apps. Many developers use the router 50 | to dictate what happens as the user navigates through the application – a substantial responsibility! 51 | 52 | Perhaps unsurprisingly, no two routers are the same. I created this chart to compare some of the features 53 | of the most popular, or otherwise noteworthy, routers. 54 | 55 | ## Why 56 | 57 | Nested routers are used in virtually every client side application these days. However, my 58 | preferred library, Backbone, does not have a nested router. In fact, you can't even build one from 59 | Backbone's router because of the way the code is structured ([I'm working to fix this in Backbone 60 | v2.0.0](https://github.com/jashkenas/backbone/pull/3660)). 61 | 62 | I want to build a new router that can be used in Backbone apps, and I want it to be the best it can 63 | be. By examining existing routers, I can pluck the features I find most useful, and discard the ones 64 | that I find less so. 65 | 66 | ## Routers Compared 67 | 68 | The following routers have been considered: 69 | 70 | - [Backbone](http://backbonejs.org/#Router) 71 | - [Ember](http://guides.emberjs.com/v1.10.0/routing/) 72 | - [React](https://github.com/rackt/react-router) 73 | - [UI-Router](https://github.com/angular-ui/ui-router) 74 | - [Stateman](https://github.com/leeluolee/stateman) 75 | - StateRouter\* 76 | 77 | \*This is a new stand-alone router that I'm building. 78 | 79 | #### Inspirations 80 | 81 | Throughout this document, I'll often draw comparisons between routers. This is usually because one of them was 82 | inspired by, and modeled after, the other. For this reason, their differences are of particular interest to me. 83 | 84 | Ember's router is the first nested router that I know of. UI-Router came shortly after, but it was largely an 85 | independent endeavor. Nevertheless, both share similarities with one another, which is to be expected 86 | considering their similar approach to routing. 87 | 88 | Each of these original two routers has since inspired another popular router. Ember inspired the React Router, 89 | and UI-Router inspired Stateman. 90 | 91 | StateRouter is the name of my work-in-progress router. It's the reason that this document exists. 92 | 93 | As a chart, the flow of influence might look something like: 94 | 95 | ``` 96 | UI-Router Ember 97 | | | 98 | Stateman React ---(all of them)---> StateRouter 99 | ``` 100 | 101 | #### Omissions 102 | 103 | I omitted certain routers from the above chart. 104 | 105 | ##### Ampersand.js 106 | 107 | Too similar to Backbone's. 108 | 109 | ##### Marionette.js 110 | 111 | Too similar to Backbone's. 112 | 113 | ##### Angular.js v1 114 | 115 | I don't think many people use this (UI Router is more popular), and it's in the process of being 116 | completely replaced in Angular v2. 117 | 118 | ##### Angular.js v2 119 | 120 | The API for this router is still in flux, and it's difficult to find documentation on how it is intended to work. 121 | 122 | ## Features 123 | 124 | ### DSL 125 | 126 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 127 | -------- | ----- | ----- | --------- | -------- | ----------- 128 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ 129 | 130 | DSL stands for "domain specific language." If you haven't heard that term before, don't worry: it's 131 | a concept you may already be familiar with, even if you don't know the name. Example DSLs are the 132 | templating languages we use to output HTML, like Handlebars, Underscore, or Angular templates. 133 | 134 | DSLs are like mini programming languages that are designed to do one task. 135 | 136 | In Routers, the DSL is the syntax you use to specify what sorts of URLs each route matches. For 137 | instance, `books/:bookId` is an example of a DSL, where the `:` designates the start of a dynamic 138 | URL segment. 139 | 140 | Every router comes with a DSL, and they share a common set of features. However, there are 141 | differences. The next few features in this list cover those differences. 142 | 143 | ##### Thoughts 144 | 145 | DSLs are a no-brainer! The alternative would be writing regular expressions for everything, which is 146 | pretty gross, I think. Given that pretty much every router supports their own DSL, it seems like 147 | everyone is in agreement on this point. 148 | 149 | ### Regex for `:dynamic` 150 | 151 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 152 | -------- | ----- | ----- | --------- | -------- | ----------- 153 | ✘ | ✘ | ✘ | ✔ | ✔ | ✔ 154 | 155 | Dynamic parts capture a single URL segment within a path. A URL segment are each of the sections 156 | separated by `/`. So, for instance, in the URL `"/books/2"`, there are two segments: `books` and 157 | `2`. 158 | 159 | Generally, dynamic segments capture **anything** between the parenthesis. But some libraries allow 160 | you to change what's matched by a dynamic segment with a regex and/or type annotation. 161 | 162 | UI-Router is the first library that I know of to do this. Because Stateman is based off of 163 | UI-Router, it, too, has a similar feature. 164 | 165 | An example in UI-Router is: 166 | 167 | ```js 168 | "/user/{id:[0-9a-fA-F]{1,8}}"" 169 | ``` 170 | 171 | UI-Router has an extra feature not found in Stateman: it allows you to specify a type, too. These 172 | are like built-in regexes that you can reference. So, for instance, `'/calendar/{start:date}'` is a 173 | valid route in UI-Router (but not Stateman). 174 | 175 | Although Backbone does not support this feature, it does support [using a regular expression for the 176 | whole route URL instead of the DSL.](#regex-instead-of-dsl). 177 | 178 | Ember and React, on the other hand, don't offer anything like this. Shucks! 179 | 180 | ##### Thoughts 181 | 182 | This feature is super useful, I think. It allows the developer to move validation into the 183 | route itself, maximizing the number of invalid requests that are automatically sent off to the 404 184 | route. 185 | 186 | When routers don't support this, the developer is forced to write custom validation logic in each 187 | route, which is more work for them. 188 | 189 | ### Splats 190 | 191 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 192 | -------- | ----- | ----- | --------- | -------- | ----------- 193 | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ 194 | 195 | Splats can be used to capture more than one segment of the URL, and are a denoted by `*`. An 196 | example is: 197 | 198 | `"/users/*splat"` 199 | 200 | This would match `/users/sandwich`, as well as `/users/james/picture`, and even 201 | `/users/james/picture/edit`. 202 | 203 | Splats are interesting because of the way different routers treat them. In Backbone and Ember, 204 | they're of particular importance, because they're the way that you specify 404 routes. 205 | 206 | `"*notFound"` 207 | 208 | This works really well for Ember, but not so well for Backbone. The difference is that Ember has a 209 | unique matching algorithm: less specific routes are matched last (see: 210 | [sort by specificity]((#sort-specificity))). So even if you specify the 404 route first, it won't be 211 | matched unless nothing else does. 212 | 213 | Backbone isn't so smart. It matches on a first come, first serve basis, which requires you to place 214 | the 404 route last. In practice, this can end up being a tedious requirement to fulfill. 215 | 216 | Other libraries use one of two other abstractions for 404s: either [a 404 state](#not-found-states), 217 | and/or [a pubsub event](#not-found-event) that is fired on the router itself. 218 | 219 | ##### Thoughts 220 | 221 | In the applications I have developed, splats have only been useful to match 404 routes. There might 222 | be some good use cases I simply haven't encountered, though, so I intend to include them in 223 | StateRouter. Additionally, I intend for them to be the algorithm behind 224 | [404 states](#not-found-states) in StateRouter. 225 | 226 | If you've used splats successfully in an app for a feature other than 404 routes, do let me know 227 | by raising an issue! I'm curious to hear the use case. 228 | 229 | Although 404 states can be implemented as thin abstraction layers over splats, I think there is 230 | value in that. An explicit 404 state is far more expressive than using splats directly to 231 | represent 404 states. 232 | 233 | ### Optional segments 234 | 235 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 236 | -------- | ----- | ----- | --------- | -------- | ----------- 237 | ✔ | ✘ | ✔ | ✔ | ✘ | ✔ 238 | 239 | Optional segments are, well, exactly what you'd expect: segments that don't need to be there. 240 | They're generally designated by a `?` or parentheses. 241 | 242 | UI-Router also supports optional segments, albeit in a different fashion. When a parameter 243 | is given a default value (which can be set when you configure your route), then it is made optional. 244 | 245 | An example from Backbone, which uses parentheses: 246 | 247 | `docs/:section(/:subsection)` 248 | 249 | ##### Thoughts 250 | 251 | These haven't been particularly useful to me, but it seems like many other developers disagree. There's 252 | [an open issue](https://github.com/tildeio/route-recognizer/issues/60) to support these in Ember, for 253 | instance. So, because I don't want StateRouter to be the only router on the street that doesn't support 254 | these things, they'll def. be in there. 255 | 256 | ### Unnamed segments 257 | 258 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 259 | -------- | ----- | ----- | --------- | -------- | ----------- 260 | ✘ | ✘ | ✘ | ✘ | ✔ | ✘ 261 | 262 | An unnamed segment is a dynamic segment, or a splat, that doesn't require an identifying name. For 263 | instance, in the route URL `"books/:bookId"`, `bookId` is the name of the dynamic segment. 264 | 265 | Stateman added this feature, although it does not exist in the UI-Router. 266 | 267 | ##### Thoughts 268 | 269 | I like being explicit, so I don't intend to add this feature to StateRouter. 270 | 271 | ### Regex instead of DSL 272 | 273 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 274 | -------- | ----- | ----- | --------- | -------- | ----------- 275 | ✔ | ✘ | ✘ | ✔ | ✘ | ✘ 276 | 277 | Backbone and UI-Router allow you to write a regular expression instead of the DSL. As you would expect, 278 | this gets ugly quickly. Because of the small amount of code necessary to implement the feature, it does 279 | fit in with Backbone's minimalist ideas. It's comparable, I think, to allowing 280 | [regex in the dynamic segments.]((#regex-for-dynamic)) 281 | 282 | ##### Thoughts 283 | 284 | This is another one of those features that is difficult for me to think up of a good use case for. If 285 | the DSL is powerful enough, it should not be necessary for a developer to use Regex directly. 286 | 287 | Nevertheless, I would be interested in supporting this feature, but it seems to be incompatible with 288 | [sorting by specificity](#sort-specificity), which I find more valuable. 289 | 290 | ### Asynchronous 291 | 292 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 293 | -------- | ----- | ----- | --------- | -------- | ----------- 294 | ✔* | ✔ | ✔ | ✔ | ✔ | ✔ 295 | 296 | Whether or not the route handlers are asynchronous. This is important for routers that support 297 | nesting, because you'll often be fetching data in your handlers. 298 | 299 | Because Backbone's handlers are independent, it simply doesn't matter if your callback is 300 | asynchronous or synchronous: nothing really *depends* on them. 301 | 302 | ##### Thoughts 303 | 304 | Considering that most routers are intended to be used to orchestrate data fetching, it only makes 305 | sense that they're built to support asynchronous handlers! 306 | 307 | ### Sort: specificity 308 | 309 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 310 | -------- | ----- | ----- | --------- | -------- | ----------- 311 | ✘ | ✔ | ✘* | ✘ | ✘ | ✔ 312 | 313 | Sorting is the order in which the router attempts to find a route. If the user has two 314 | routes corresponding to the following two URLs: 315 | 316 | `books/:bookId` and `books/author` 317 | 318 | Which will match when the user navigates to `books/author`? Does it depend on the order 319 | that they are added? 320 | 321 | For most routers, the answer to the above question is **yes**. `books/author` will 322 | need to be added to the router before `books/:bookId`, or else the dynamic segment 323 | will always match the string. 324 | 325 | For routers that support [regex for dynamic segments](#regex-for-dynamic), this matters less because 326 | that feature provides a way to make your dynamic segments even more specific. 327 | 328 | Ember and my own router will implement this feature. 329 | 330 | React Router uses an algorithm that indirectly leads to sorting by specificity. Their 331 | algorithm attempts to match [the deepest route first.](#sort-deepest) 332 | 333 | ##### Thoughts 334 | 335 | :+1:! 336 | 337 | 338 | ### Sort: deepest 339 | 340 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 341 | -------- | ----- | ----- | --------- | -------- | ----------- 342 | ✘ | ✘* | ✔ | ✘ | ✘ | ✘* 343 | 344 | Sorting by the deepest route is similar to sorting by specificity. It tries to 345 | match the most-nested routes first. 346 | 347 | Because deeper routes tend to be more specific, Ember's algorithm approximates 348 | this one. 349 | 350 | ##### Thoughts 351 | 352 | Sort by specificity is more intuitable, I think, so I intend to use that instead. 353 | 354 | ### Sort: order added 355 | 356 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 357 | -------- | ----- | ----- | --------- | -------- | ----------- 358 | ✔ | ✘ | ✘ | ✔ | ✔ | ✘ 359 | 360 | All other routers sort by the order added. Therefore, you must specify your dynamic segments and 361 | splats last, or use regexes to restrict what your dynamic segments match. 362 | 363 | ##### Thoughts 364 | 365 | This is easier to implement than sorting by specificity, but more difficult for developers to work 366 | with. However, for Routers that allow you override the regex used by dynamic segments, this is less 367 | of an issue. 368 | 369 | ### Nested routes 370 | 371 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 372 | -------- | ----- | ----- | --------- | -------- | ----------- 373 | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ 374 | 375 | Are the routes related? Does entering a child route cause you to first enter the parent route? If 376 | yes, then it's a nested router. 377 | 378 | Given that routes encode your app's *location* (which is often represented by a URL), it's no 379 | surprise that most routers these days are nested. 380 | 381 | Backbone is the exception here. 382 | 383 | ##### Thoughts 384 | 385 | All contemporary routers support nesting because it is a super useful feature! 386 | 387 | ### Reset Namespace 388 | 389 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 390 | -------- | ----- | ----- | --------- | -------- | ----------- 391 | ✘ | ✔ | ✘ | ✘ | ✘ | ✘ 392 | 393 | Most routers require that children routes be built from their parent's namespace. For instance, if 394 | you want to add a child route to `books.book`, it must necessarily begin with `books.book`. A route 395 | called `comments` could not, for instance, be a child of `books.book`. 396 | 397 | ##### Thoughts 398 | 399 | I don't think that this is particularly useful. A friend of mine on the Ember Data team suggests 400 | that nobody use this feature, which is pretty telling, I think. 401 | 402 | ### Absolute URLs 403 | 404 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 405 | -------- | ----- | ----- | --------- | -------- | ----------- 406 | ✔ | ✘ | ✔ | ✔ | ✔ | ✔ 407 | 408 | A child route's URL is often appended to the parent's route. Are you able to specify that the child's 409 | URL is an absolute URL? 410 | 411 | Backbone's URLs are independent, so, yes, they are all absolute. 412 | 413 | In UI-Router and Stateman, you can use the `^` prefix to indicate that the URL is absolute. 414 | 415 | In React Router, prefixing with `/` indicates an absolute URL, whereas no prefix is not an 416 | absolute route. 417 | 418 | ##### Thoughts 419 | 420 | I haven't worked on an app that would use this, but it is used by React and UI-Routers, so I'll 421 | likely include it. 422 | 423 | ### Optional history 424 | 425 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 426 | -------- | ----- | ----- | --------- | -------- | ----------- 427 | ✘ | ✔ | ✘ | ✔ | ✘ | ✔ 428 | 429 | Because a nested router is just a state machine, it should not logically need to depend on URLs to 430 | function. This is useful in apps that don't encode the location of the user in the URL, like 431 | embedded apps. 432 | 433 | Interestingly, Ember and the UI-Router are the only two that support this feature. The two libraries 434 | that were inspired by these libraries, React's Router and Stateman, seem to have foregone including 435 | it. 436 | 437 | ##### Thoughts 438 | 439 | This feature is a must! It's a shame that the React Router and Stateman didn't recognize the value. 440 | 441 | ### Includes history 442 | 443 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 444 | -------- | ----- | ----- | --------- | -------- | ----------- 445 | ✔ | ✔ | ✔ | ✔* | ✔ | ✘ 446 | 447 | Whether or not the library includes a History implementation (to support, say, hash changes in old 448 | browsers). 449 | 450 | Generally, the routers that are bundled with a framework do include a history implementation. 451 | 452 | UI-Router uses Angular's `$location` server. 453 | 454 | ##### Thoughts 455 | 456 | Very useful for routers that are part of a framework. Because StateRouter is just a router, it 457 | doesn't make sense to bundle up a history implementation for it. A Backbone-specific version will 458 | be subsequently released that ties in nicely with Backbone's history. Otherwise, you'll need 459 | to wire up your own history, which won't be too hard. 460 | 461 | ### Cancel navigation 462 | 463 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 464 | -------- | ----- | ----- | --------- | -------- | ----------- 465 | ✔* | ✔ | ✔ | ✔ | ✔ | ✔ 466 | 467 | Whether or not there's an abstraction for canceling navigation. 468 | 469 | In Ember and React Router you get the opportunity for each route to specify whether the transition 470 | should be cancelled or not. 471 | 472 | In UI-Router and Stateman, an event is fired on the router itself that allows you to cancel the 473 | transition, so it's more like a global setting. v1 of the UI-Router (unreleased as of 8/16) will 474 | allow cancellation at any level of granularity. 475 | 476 | Backbone provides a method that you can return false from to prevent transitioning. 477 | 478 | ##### Thoughts 479 | 480 | Per-route cancellation is fantastic. Global cancellation...less so. 481 | 482 | ### Redirect navigation 483 | 484 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 485 | -------- | ----- | ----- | --------- | -------- | ----------- 486 | ✔ | ✔ | ✔ | ✔* | ✔ | ✔ 487 | 488 | Very similar to the above: can you stop the transition and instead redirect? The same hooks used for 489 | canceling is used for redirection in all libraries. 490 | 491 | The UI-Router had 492 | [a bad bug with redirecting](https://github.com/angular-ui/ui-router/issues/326#issuecomment-52731196) 493 | the last time I used it, and I do not believe that it has been fixed. 494 | 495 | ##### Thoughts 496 | 497 | Same as above. 498 | 499 | ### Template links 500 | 501 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 502 | -------- | ----- | ----- | --------- | -------- | ----------- 503 | ✘ | ✔ | ✔ | ✔ | ✘ | ✘ 504 | 505 | Some libraries provide an abstraction to be used in your template to generate links that are 506 | automagically hooked up to the router. 507 | 508 | This feature is super rad – if you haven't used it before you should play with one of the routers 509 | that support it. 510 | 511 | ##### Thoughts 512 | 513 | So, so good, but out of scope for a standalone router, I think. I plan to build a version of 514 | StateRouter exclusively for Backbone apps, and it will have templating features like this. 515 | 516 | ### `.active` links 517 | 518 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 519 | -------- | ----- | ----- | --------- | -------- | ----------- 520 | ✘ | ✔ | ✔ | ✔ | ✘ | ✘ 521 | 522 | Does the router automatically append an active class to the links in the template when that state is 523 | entered? 524 | 525 | ##### Thoughts 526 | 527 | :heart_eyes: Although I love this feature, it won't be in the core StateRouter library, given just 528 | how many templating languages there are out there. 529 | 530 | ### Query params 531 | 532 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 533 | -------- | ----- | ----- | --------- | -------- | ----------- 534 | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ 535 | 536 | Coming soon... 537 | 538 | ##### Thoughts 539 | 540 | Coming soon... 541 | 542 | ### 'Index' states 543 | 544 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 545 | -------- | ----- | ----- | --------- | -------- | ----------- 546 | ✘ | ✔ | ✔ | ✘ | ✘ | ✔ 547 | 548 | To understand index states, you must first know that nested routers involve states being active 549 | or inactive. Because they are nested, when a child is landed on, all of its parents are *also* 550 | active. 551 | 552 | Index states are special states that can be associated with each state. They're only activated 553 | when their associated state is landed on directly. 554 | 555 | I understand that this may be confusing, so let's look at an example. 556 | 557 | Consider three routes, 558 | 559 | `books` 560 | `books.index` 561 | `books.book` 562 | 563 | Given these states, landing on "books" will activate both books and books.index. 564 | 565 | However, landing on "books.book" will activate both "books" and "books.book", but not "books.index" 566 | 567 | ##### Thoughts 568 | 569 | :+1: This is a super useful feature to better control the fetching of data. 570 | 571 | ### 'Abstract' states 572 | 573 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 574 | -------- | ----- | ----- | --------- | -------- | ----------- 575 | ✘ | ✘ | ✘ | ✔ | ✘ | ✘ 576 | 577 | Abstract states are maybe even more difficult to understand than index states. Abstract states are 578 | states that cannot ever be landed on directly. Instead, they can only be activated when their child 579 | states are. 580 | 581 | This gives you a hook to, say, load a particular set of data for a group of sibling routes. 582 | 583 | ##### Thoughts 584 | 585 | It's my understanding that these provide the same value as index states, but in a way that I find 586 | to be more confusing. 587 | 588 | ### 'Loading' states 589 | 590 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 591 | -------- | ----- | ----- | --------- | -------- | ----------- 592 | ✘ | ✔ | ✘ | ✘ | ✘ | ✔ 593 | 594 | While transitions are in progress some routers give you a `loading` state to display. 595 | 596 | ##### Thoughts 597 | 598 | :+1: 599 | 600 | ### 'Error' states 601 | 602 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 603 | -------- | ----- | ----- | --------- | -------- | ----------- 604 | ✘ | ✔ | ✘ | ✘* | ✘* | ✔ 605 | 606 | When an error occurs when doing a transition, some routers allow you to specify an `error` state to 607 | enter. 608 | 609 | UI-Router and Stateman provide events to manage this. 610 | 611 | ##### Thoughts 612 | 613 | :+1: 614 | 615 | ### 'Not Found' states 616 | 617 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 618 | -------- | ----- | ----- | --------- | -------- | ----------- 619 | ✘ | ✘ | ✔ | ✘ | ✔* | ✔ 620 | 621 | Not found states let you specify unique not found states for each portion of your 622 | application. 623 | 624 | Stateman provides a not found state that only exists on the root level, so it's substantially 625 | less useful than what is provided by React. 626 | 627 | ##### Thoughts 628 | 629 | Very useful when done on a per-route basis. 630 | 631 | ### Pubsub 632 | 633 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 634 | -------- | ----- | ----- | --------- | -------- | ----------- 635 | ✔ | ✘ | ✘ | ✔ | ✔ | ✘ 636 | 637 | Whether or not you can do something like: 638 | 639 | `router.on('event', myCallback)` 640 | 641 | These routers are generally the ones that give you global hooks to catch errors and 404 states, as 642 | those hooks are events. 643 | 644 | ##### Thoughts 645 | 646 | If the router is for another library that has pubsub, then I think that supplying events for 647 | consistency's sake makes sense. Otherwise, I think the majority of routing features don't benefit 648 | much from including pubsub. 649 | 650 | One issue with pubsub is that everything is globally configured. Instead of associating logic 651 | with a particular route, it requires that you use if-then statements in a global callback to 652 | specify per-route behaviors. 653 | 654 | ### Not found event 655 | 656 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 657 | -------- | ----- | ----- | --------- | -------- | ----------- 658 | ✘ | ✘ | ✘ | ✔ | ✔ | ✘ 659 | 660 | Not found events are triggered on the router directly, and give you a global hook to manage 661 | 404s. This is less powerful than Not Found states, because it requires that you place all of your 662 | 404 logic within a single callback. 663 | 664 | ##### Thoughts 665 | 666 | :-1: 667 | 668 | ### Scrolling 669 | 670 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 671 | -------- | ----- | ----- | --------- | -------- | ----------- 672 | ✘ | ✘ | ✔ | ✔* | ✘ | ? 673 | 674 | Browsers remember your scroll position when you navigate an app with the Back and Forward buttons. 675 | They also scroll you to the top of the page when a new link is clicked. 676 | 677 | This feature is about whether or not the router supports this out of the box. 678 | 679 | The UI-Router is markedly different: it allows you to scroll to a particular element when the route 680 | changes. 681 | 682 | ##### Thoughts 683 | 684 | Definitely useful, but I'm torn on whether or not it's a good idea to bundle this into the router 685 | directly! 686 | 687 | UI-Router's scrolling feature might be useful in some situations, but I don't think that it's 688 | useful as a default, or even as a feature to include in a core routing library. 689 | 690 | ### Group data fetching 691 | 692 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 693 | -------- | ----- | ----- | --------- | -------- | ----------- 694 | ✘ | ✔ | ✔* | ✘* | ✘ | ? 695 | 696 | If 3 routes are activated, and each specify data to be fetched, does all of the fetching happen 697 | before views start rendering? 698 | 699 | Ember does this out of the box. React, being just a view layer, naturally doesn't have any APIs 700 | directly related to fetching data. But with some pretty messy boilerplate code you can pull it off 701 | (see the examples under [the Router.run method](http://rackt.github.io/react-router/#Router.run)) 702 | 703 | UI-Router's unreleased v1 will allow you to specify 'policies' to manage this. 704 | 705 | ##### Thoughts 706 | 707 | I'm on the fence about this one. I like the idea of fetching all of the data first to run the XHRs 708 | concurrently, but I'm worried that it may make passing each route the data that it requested more 709 | difficult. 710 | 711 | I'm leaning more toward grouping them, though. 712 | 713 | ### Enter hook 714 | 715 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 716 | -------- | ----- | ----- | --------- | -------- | ----------- 717 | ✔ | ✔ | ✔* | ✔ | ✔ | ✔ 718 | 719 | Does a callback fire when the route is entered? 720 | 721 | In Backbone, this is all that you get. 722 | 723 | In React, from what I understand it sort of just implicitly renders the component. I'm not sure if 724 | there's much else that you'd want to do (or could do), but I'm only gathering this from their docs 725 | as I haven't used their router. 726 | 727 | In Ember, this is a no-op that you can use to include additional logic. 728 | 729 | ##### Thoughts 730 | 731 | A good idea. 732 | 733 | ### Exit hook 734 | 735 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 736 | -------- | ----- | ----- | --------- | -------- | ----------- 737 | ✘ | ✔ | ✘ | ✔ | ✔ | ✔ 738 | 739 | When a route is being transitioned out, do you get a hook to do things? 740 | 741 | One thing to note is that this seems to be conflated with the ability to cancel/redirect in certain 742 | routers. In the React Router, for instance, there's no distinct "exit" hook, but there is a hook 743 | called before you leave, which is intended for canceling. I don't know enough about React to know 744 | whether this is a good or a bad thing! 745 | 746 | In Backbone, there's no such thing as a route being 'active' or 'inactive.' Routes are just 747 | callbacks out-of-the-box. 748 | 749 | ##### Thoughts 750 | 751 | Very useful for teardown when you're working with a nested router. 752 | 753 | ### Update hook 754 | 755 | Backbone | Ember | React | UI-Router | Stateman | StateRouter 756 | -------- | ----- | ----- | --------- | -------- | ----------- 757 | ✘ | ✔ | ✘ | ✘ | ✔ | ✔ 758 | 759 | An update hook is distinct from an enter hook, in that it is generally related to whether the 760 | "context" of a route changes. Consider a route with a URL `:bookId`. When the user transitions to 761 | this same route, but with a different ID, what happens? 762 | 763 | In Ember, the update hook is the most important hook. It's where the view is rendered, for instance. 764 | The update hook in Ember is called `setup`. And the order of callbacks goes 765 | `exit => enter => setup` 766 | 767 | `exit` starts at the deepest route, and works its way up. `enter` and `setup` work their way 768 | down the chain. `setup` begins on the first route whose context is changed. 769 | 770 | Stateman operates in a different way. The order goes `exit => update => enter`. And, unlike 771 | Ember, `update` is called on any route that is unchanged, even if the context hasn't changed. 772 | Also, the `update` hook in Stateman works its way the tree, like the `exit` hook. 773 | 774 | ##### Thoughts 775 | 776 | :+1: It is useful to differentiate entering and updating, I think. I tend to prefer Ember's 777 | hook structure. 778 | --------------------------------------------------------------------------------