This closes emberjs#9814 and closes emberjs#10304, which are examples of a class of
problems caused by the way the router synchronously reaches into the
view hierarchy to set outlet state. It is a significant refactor of the
way the router communicates state to outlets.
What motivates this change?
- Simple examples like `{{#if something}}{{outlet}}{{/if}}` are
incorrect under the current model.
- Richer examples like block-helpers to enable animation also
suffer. In general, the router cannot know when and whether a
particular outlet is going to exist, and it shouldn't need to know.
- The router maintains a bunch of view-related state that is actually
redundant with the view hierarchy itself, leading to unnecessary
complexity.
- This eliminates the longstanding weirdness & confusion caused by the
fact that we used to create new `View` instances and then throw them
away if they looked similar enough to ones that were already
rendered. That functionality is now covered by state diffing in the
`OutletView`.
- We reduce the API surface area between router and view layer in a way
that should make it easier to experiment with swapping in compatible
implementations of either.
- As a bonus, this changes makes outlets work in an observer-free way
that will make them easy to integrate with upcoming planned view
layer optimizations.
How does this work?
- Rather than directly building and linking views, the router builds up
an abstract summary of the render decisions that have been made by
the current routes.
- This state is cheap to recalculate as needed. It doesn't do any view
creation. To avoid expensive observer creation & teardown, we just
recreate the whole thing and use a `setState`-like mechanism to
propagate the changes through the outlet hierarchy. This gives us
optimal granularity of updates.
- Actual view instantiation moves into the OutletView -- within the
view layer where it belongs. Each outlet does a diff to see whether
it should rerender itself or propagate inner changes down to its
child outlets.
- To bootstrap rendering, the router creates a single top-level outlet,
after which all view creation is internal to the view layer.
Does this break any existing semantics?
- No, as far as I can tell.
Could this get even better if we decided to deprecate some old
semantics?
- Yes. It would be better if users` `renderTemplate` implementations on
`Route`s were required to be idempotent. Then we could eliminate a
bunch of the remaining state from them.
- Also, when we deprecate the `render` helper we can eliminate the
remaining use of `_activeViews` state tracking on the router. That is
the only remaining use for it.