摘要

  • 移除 tag prop
  • 移除 event prop
  • 停止自动分配点击事件给内部锚点
  • 添加一个作用域插槽 API
  • 添加一个 custom 的 prop,以完全自定义 router-link 的方式渲染

基本范例

  1. <router-link to="/">
  2. <Icon>home</Icon> Home
  3. </router-link>

动机

当前实现的 Router Link 有许多限制:

  • active 状态定制不完整(#2611
  • 不能与自定义组件集成(#2021
  • 不能防止点击事件(通过 @click.prevent 或通过 disable 属性 #2098

这个 RFC 的想法是通过提供一个作用域插槽来解决这些问题,允许开发者根据它们的应用轻松的扩展 Links,并允许库作者提供一个更容易的与 Vue Router 的集成。

具体设计

内容插槽

一个简单的用例是带有内容的插槽(那样嵌套的锚点或者按钮)

  1. <router-link to="/">
  2. <Icon>home</Icon> Home
  3. </router-link>

这种实现将:

  • 生成一个锚点(a)元素并应用相应的属性:
    • 带有目的地的 href
    • 带有 router-link- active 或 router-link-active 的类(可以通过 prop 或全局选项改变)
    • 点击监听器,通过 router.push 或 router.replace 与 event.preventDefault 来触发导航(除了当前连接被点击时使用像 ⌘ 或 Ctrl 这样的修饰词)
  • 把任何传递的东西作为锚点的子元素
  • 把任何不是 props 的特性传递给 a 元素

破坏性改变:

  • 不再接受 tag 属性 -> 使用作用域插槽来代替(见下文)
  • 不再接受事件 -> 使用作用域插槽来代替
  • 不再作为一个包装器自动寻找里面的第一个 a -> 使用作用域插槽来代替

作用域插槽

一个作用域插槽将获得提供自定义集成所需的每一点信息,并允许任何级别的 active 类、click 监听器、链接等等。这将允许与 Bootstrap(https://getbootstrap.com/docs/4.3/components/navbar/)等 UI 框架更好的整合。我们的想法时创建一个 Vue 组件来避免像 bootstrap-vue 那样的模版(https://bootstrap-vue.js.org/docs/components/navbar/#navbar)。

  1. <router-link to="/" custom v-slot="{ href, navigate, isActive }">
  2. <li :class="{ 'active': isActive }">
  3. <a :href="href" @click="navigate">
  4. <Icon>home</Icon><span class="xs-hidden">Home</span>
  5. </a>
  6. </li>
  7. </router-link>

custom 属性对完全控制 router-link 的渲染是必要的:不渲染包装的 a 元素。

为什么需要 custom 属性:在 Vue3 中,作用域插槽和普通插槽不能互相区分,这意味着 vue router 无法区分这三种情况:

  1. <router-link to="/" v-slot="{ href, navigate, isActive }"></router-link>
  2. <router-link to="/" v-slot></router-link>
  3. <router-link to="/">Some Link</router-link>

在这三种情况下,我们都需要渲染插槽的内容,但 router-link 需要知道它是否需要渲染一个包裹的元素。在 Vue2 中,我们可以通过检查 $scopedSlots 来做到这一点,但在 Vue3 中,只有 slots 存在。这意味着在 Vue Router v3 和 Vue Router v4 中的行为略有不同:

  • 在 v3 中,custom 属性是必须的(见采纳策略),与 v-slot 一起。router-link 不会用 a 元素来包装插槽内容。
  • 在 v4 中,custom 属性不需要与 v-slot 一起使用。它控制 router-link 是否应该用 a 元素来包装它的插槽内容。 ```vue {{ href }} / /
  1. <a name="JFgXa"></a>
  2. ### 可访问的变量
  3. 插槽应该提供在 `router-link` 内部计算的值:
  4. - `href`:解析后的相对 url,将被添加到一个锚点标签中(如果提供的话,包含基础,而 route.pullPaht 不包含)
  5. - `route`:从 `to` 中解析出规范化的路由位置(与 $route 的形状相同)
  6. - `navigate`:触发导航的函数(通常与点击相连)。如果点击被直接按下,也会调用 `preventDefault`
  7. - `isActive`:每当应用 `router-link-active` 就是 true。可以通过 `exact` 属性修改
  8. - `isExcatActive`:在应用 `router-link-active` 时为 true。可以通过 `exact` 属性修改
  9. <a name="GCcXQ"></a>
  10. ## 移除 `tag` 属性
  11. `tag` 属性可以被替换成一个作用域插槽,并使代码更清晰,同时不暴露在任何警告之中。它的移除也将减轻 vue-router 库的负担。
  12. ```vue
  13. <router-link to="/" tag="button">
  14. <Icon>home</Icon><span class="xs-hidden">Home</span>
  15. </router-link>

相当于

  1. <router-link to="/" custom v-slot="{ navigate, isActive, isExactActive }">
  2. <button role="link" @click="navigate" :class="{ active: isActive, 'exact-active': isExactActive }">
  3. <Icon>home</Icon><span class="xs-hidden">Home</span>
  4. </button>
  5. </router-link>

(关于传递给作用域插槽的属性,见上面的解释)

缺点

  • 虽然有可能保持现有的行为,只用作用域插槽来暴露一个新的行为,但这仍然会妨碍我们修复当前实现的现有问题。这就是为什么有一些破坏性的变化,以使得事情更加一致。
  • 不能访问默认的 router-link 类,如 router-link-activerouter-link-excat-link

备选方案

  • 为方便起见,保留 event 属性
  • 使用一个不同的具名插槽来替代属性: ```vue

`` 在这种情况下,采用的策略将是类似的,但警告将告诉用户使用不同的插槽,而不是使用名为custom` 的属性。

  • 创建一个新的组件,如 router-link-custom 来区分行为。然而, 这个解决方案比一个属性火一个不同的命名插槽更重(就尺寸而言)。它也没有属性那么合适,因为我们只是改变了组件的一个行为。这两个组件之间的差异太小,不足以证明一个全新的组件是合理的。

采纳策略

  • 根据实例记录新插槽的行为。
  • 在 v3 版本中,用一条信息废除 tagevent,并链接到文档,然后在 v4 版本中删除。
  • 在 v3 中,如果使用作用域插槽时没有提供 custom 属性,则警告用户使用 custom 属性。

没有解决的问题

N/A