recorded decision · public · no signup
Summary
What was chosen
What was ruled out· 1
- A `useView` composition function was considered but rejected because it was significantly more complicated than a simple prop.adr ↗
Constraints
- Nested `router-view` components must consistently use the location provided by the `route` prop from their parent.adr ↗
- Implementing custom solutions for displaying views not connected to the URL is difficult without modifying `this.$route`.adr ↗
- Vue Router's default design, associating a URL with a component, limits certain advanced use cases like modals.adr ↗
Consequences
- The `route` prop enables users to display a view that is not directly connected to the current URL.adr ↗
- The `route` prop facilitates implementing patterns such as modals within Vue Router applications.adr ↗
- Exposing a `route` prop to modify `router-view` display is straightforward from an implementation perspective.adr ↗
- The `route` prop mechanism requires the resolved route to have been navigated to previously for lazy loading to function correctly.adr ↗
The recorded why
- Start Date: 2020-04-01
- Target Major Version: Vue Router v3 and v4
- Reference Issues:
- Implementation PR:
Summary
Add a route prop that allows customizing what is displayed by the router-view component. This allows users to display a view that is not the one connected to the URL. It enables patterns like modals (https://github.com/vuejs/vue-router/issues/703#issuecomment-428123334)
Basic example
Imagine a backgroundView variable that allows us to know if we are displaying a different route. We could then compute a route location that would resolve to a different location than the one displayed on the url:
const App = {
computed: {
routeWithModal() {
if (backgroundView) {
return this.$router.resolve(backgroundView)
} else {
return this.$route
}
}
}
}
We could pass that variable to router-view:
<router-view :route="routeWithModal"></router-view>
This will in most scenarios display the component associated with the current url (this.$route), and in others display something different while still exposing the resolved location at this.$route.
Motivation
By design, Vue Router associates a URL with a Component. This is sometimes a limitation, like with modals, and this allows escaping the limitation. It's very hard to implement a userland solution without hacks (like modifying this.$route at this example) because it involves changing the current location. Exposing a way to modify what is displayed by router-view is straightforward from an implementation point of view.
Detailed design
Vue Router already exposes the mechanism to resolve a location so users can use some global state (ideally it should be held in window.history.state so it is restored when navigating using the browser back and forward buttons) to generate a resolved location that can be consumed by router-view.
Internally, router-view must make sure its children (nested views) use that same location to be consistent. In v4, using inject/provide should make this simple, in v3, we might need to look up like we do for depth. If a route prop is provided, it takes precedence against any providen route on the router-view receiving the prop and all its children.
The end user API is one single prop:
<router-view :route="routeWithModal"></router-view>
Drawbacks
- For this mechanism to work with Lazy Loading, it requires for the resolved route to have been navigated to before. This ensures any lazy view has been already fetched and catched. In the scenario of modals, this is automatic because we are already on the view we want to make
router-viewdisplay. I think this limitation makes sense because I cannot think of other use cases apart from Modals when it comes to displaying a view that is not associated to the URL.
Alternatives
- A composition function
useViewthat returns a reactive component to be used with<component :is="resultOfUseView"/>. This solutions is way more complicated than a prop.