Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e07eeac

Browse filesBrowse files
authored
Amendments to RouterLink scoped-slot (#152)
1 parent 86a2245 commit e07eeac
Copy full SHA for e07eeac

File tree

Expand file treeCollapse file tree

1 file changed

+70
-27
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+70
-27
lines changed

‎active-rfcs/0021-router-link-scoped-slot.md

Copy file name to clipboardExpand all lines: active-rfcs/0021-router-link-scoped-slot.md
+70-27Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Remove `event` prop
1010
- Stop automatically assigning click events to inner anchors
1111
- Add a scoped-slot API
12+
- Add a `custom` prop to fully customize `router-link`'s rendering
1213

1314
# Basic example
1415

@@ -55,34 +56,12 @@ This implementation would:
5556
- no longer accepts `event` -> use the scoped slot instead
5657
- no longer works as a wrapper automatically looking for the first `a` inside -> use the scoped slot instead
5758

58-
## Custom `tag` prop
59-
60-
I am not sure about keeping the `tag` prop if it can be replaced which a scoped slot because it wouldn't handle custom components and except for very simple cases, we will likely use custom UI components instead of the basics ones:
61-
62-
```vue
63-
<router-link to="/" tag="button">
64-
<Icon>home</Icon><span class="xs-hidden">Home</span>
65-
</router-link>
66-
```
67-
68-
is equivalent to
69-
70-
```vue
71-
<router-link to="/" v-slot="{ navigate, isActive, isExactActive }">
72-
<button role="link" @click="navigate" :class="{ active: isActive, 'exact-active': isExactActive }">
73-
<Icon>home</Icon><span class="xs-hidden">Home</span>
74-
</button>
75-
</router-link>
76-
```
77-
78-
(see below for explanation about the attributes passed to the scoped-slot)
79-
8059
## Scoped slot
8160

82-
A scoped slot would get access to every bit of information needed to provide a custom integration and allows applying the active classes, click listener, links, etc at any level. This would allow a better integration with frameworks like Bootstrap (https://getbootstrap.com/docs/4.3/components/navbar/). The idea would be to create a Vue component to avoid the boilerplate like bootstrap-vue does (https://bootstrap-vue.js.org/docs/components/navbar/#navbar)
61+
A scoped slot would get access to every bit of information needed to provide a custom integration and allows applying the active classes, click listener, links, etc at any level. This would allow a better integration with UI frameworks like Bootstrap (https://getbootstrap.com/docs/4.3/components/navbar/). The idea would be to create a Vue component to avoid the boilerplate like bootstrap-vue does (https://bootstrap-vue.js.org/docs/components/navbar/#navbar)
8362

8463
```vue
85-
<router-link to="/" v-slot="{ href, navigate, isActive }">
64+
<router-link to="/" custom v-slot="{ href, navigate, isActive }">
8665
<li :class="{ 'active': isActive }">
8766
<a :href="href" @click="navigate">
8867
<Icon>home</Icon><span class="xs-hidden">Home</span>
@@ -91,6 +70,30 @@ A scoped slot would get access to every bit of information needed to provide a c
9170
</router-link>
9271
```
9372

73+
The `custom` prop is necessary to take full control over `router-link`'s rendering: not rendering a wrapping `a` element.
74+
75+
**Why is a `custom` prop necessary**: in Vue 3, scoped slots and regular slots cannot be differentiated from each other, which means vue router is unable to make the difference between these 3 cases:
76+
77+
```vue
78+
<router-link to="/" v-slot="{ href, navigate, isActive }"></router-link>
79+
<router-link to="/" v-slot></router-link>
80+
<router-link to="/">Some Link</router-link>
81+
```
82+
83+
In all three cases we need to render the slot content but `router-link` needs to know if it has to render a wrapping `a` element. In Vue 2, we are able to do so by checking `$scopedSlots` but in Vue 3, only `slots` exists. This means that the behavior is slightly different in Vue Router v3 and Vue Router v4:
84+
85+
- In v3, the `custom` prop is required (see [Adoption strategy](#adoption-strategy)) alongside `v-slot`. `router-link` will not wrap the slot content with an `a` element.
86+
- In v4, the `custom` prop is **not** required alongside `v-slot`. It controls whether `router-link` should wrap its slot content with an `a` element or not:
87+
```vue
88+
<router-link to="/" v-slot="{ href }">
89+
<router-link to="/" custom v-slot="{ href, navigate }">
90+
<a :href="href" @click="navigate">{{ href }}</a>
91+
</router-link>
92+
<!-- both render the same -->
93+
<a href="/">/</a>
94+
<a href="/">/</a>
95+
```
96+
9497
### Accessible variables
9598

9699
The slot should provide values that are computed inside `router-link`:
@@ -101,6 +104,28 @@ The slot should provide values that are computed inside `router-link`:
101104
- `isActive`: true whenever `router-link-active` is applied. Can be modified by `exact` prop
102105
- `isExactActive`: true whenever `router-link-exact-active` is aplied. Can be modified by `exact` prop.
103106

107+
## The removal of the `tag` prop
108+
109+
The `tag` prop can be replaced which a scoped slot and make the code clearer while not being exposed to any caveat. Its removal will also lighten the vue-router library.
110+
111+
```vue
112+
<router-link to="/" tag="button">
113+
<Icon>home</Icon><span class="xs-hidden">Home</span>
114+
</router-link>
115+
```
116+
117+
is equivalent to
118+
119+
```vue
120+
<router-link to="/" custom v-slot="{ navigate, isActive, isExactActive }">
121+
<button role="link" @click="navigate" :class="{ active: isActive, 'exact-active': isExactActive }">
122+
<Icon>home</Icon><span class="xs-hidden">Home</span>
123+
</button>
124+
</router-link>
125+
```
126+
127+
(see above for explanation about the attributes passed to the scoped-slot)
128+
104129
# Drawbacks
105130

106131
- Whereas it's possible to keep existing behaviour working and only expose a new behaviour with scoped slots, it will still prevent us from fixing existing issues with current implementation. That's why there are some breaking changes, to make things more consistent.
@@ -109,10 +134,28 @@ The slot should provide values that are computed inside `router-link`:
109134
# Alternatives
110135

111136
- Keeping `event` prop for convienience
137+
- Use a different named slot instead of a `prop`:
138+
139+
```vue
140+
<router-link #custom="{ href }">
141+
<a :href="href"></a>
142+
</router-link>
143+
144+
<router-link v-slot:custom="{ href }">
145+
<a :href="href"></a>
146+
</router-link>
147+
148+
<router-link custom v-slot="{ href }">
149+
<a :href="href"></a>
150+
</router-link>
151+
```
152+
153+
The adoption strategy in this case would be similar but the warning would tell the user to use a different slot instead of a prop named `custom`
154+
155+
- Create a new component like `router-link-custom` to differentiate the behavior. This solution is however heavier (in terms of size) than a prop or a different named slot. It is also less suitable than a prop because we are only changing a behavior of the component. The difference between the two components woud be too small to justify a whole new component.
112156

113157
# Adoption strategy
114158

115159
- Document new slot behaviour based on examples
116-
- Deprecate `tag` and `event` with a message and link to documentation the remove in v4
117-
118-
# Unresolved questions
160+
- Deprecate `tag` and `event` with a message in v3 and link to documentation, then remove in v4
161+
- In v3, if no `custom` prop is provided when using a scoped slot, warn the user to use the `custom` prop

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.