【问题标题】:How to handle anchors (bookmarks) with Vue Router?如何使用 Vue Router 处理锚点(书签)?
【发布时间】:2017-12-25 08:12:59
【问题描述】:

我正在寻找一种使用 Vue Router 处理页内锚点的智能方法。考虑以下几点:

<router-link to="#app">Apply Now</router-link>
<!-- some HTML markup in between... -->
<div id="app">...</div>

“滚动到锚点”行为described in the docs 工作正常,除了:

  • 当您单击锚点时,它会将您带到div id="app"。现在从div 滚动到锚点并再次尝试单击它——这次您将不会跳到div。事实上,anchor 会保留 router-link-active 类,并且 URL 仍然会包含哈希 /#app
  • 通过上述步骤,如果刷新页面(URL 仍将包含哈希)并单击锚点,也不会发生任何事情。

从用户体验的角度来看,这是非常不幸的,因为潜在客户必须再次手动向下滚动才能到达应用程序部分。

我想知道 Vue Router 是否涵盖了这种情况。作为参考,这是我的路由器:

export default new VueRouter({
    routes,
    mode: 'history',
    scrollBehavior(to, from, savedPosition) {
        if (to.hash) {
            return { selector: to.hash }
        } else if (savedPosition) {
            return savedPosition;
        } else {
            return { x: 0, y: 0 }
        }
    }
})

【问题讨论】:

    标签: vue.js vuejs2 vue-router


    【解决方案1】:

    我知道你问过关于主播的问题,你已经有了答案。但是,以下功能应该适用于锚点和常规链接。它允许您滚动到与路由匹配的组件的第一个实例的位置。我写它是为了看看是否可以在保留锚样式滚动的同时绕过使用哈希。

    scrollBehavior(to, from, savedPosition) {
      if (to.matched) {
        const children = to.matched[1].parent.instances.default.$children;
        for (let i = 0; i < children.length; i++) {
          let child = children[i];
          if (child.$options._componentTag === to.matched[1].components.default.name) {
            return {
              x: child.$el.offsetLeft,
              y: child.$el.offsetTop
            };
          }
        }
      }
      return {
        x: 0,
        y: 0
      };
    }
    

    我使用parent.instances 的原因是因为to.matched[1].instances 的值是空的。这不是最优雅的解决方案,尽管它可能会帮助其他人。

    注意:这仅在您想要滚动组件的第一个实例时才有效。

    【讨论】:

      【解决方案2】:

      我在资源中没有找到任何可以解决您的问题的内容,但您可以在包含您的&lt;router-view&gt;&lt;/router-view&gt; 的组件的mounted 挂钩中使用$route.hash 来解决刷新问题。

      <script>
      export default {
        name: 'app',
        mounted: function()
        {
          // From testing, without a brief timeout, it won't work.
          setTimeout(() => this.scrollFix(this.$route.hash), 1);
        },
        methods: {
          scrollFix: function(hashbang)
          {
            location.hash = hashbang;
          }
        }
      }
      </script>
      

      然后要解决第二次点击的问题,您可以使用native 修饰符并绑定到您的&lt;router-link&gt;&lt;/router-link&gt;。这是一个相当手动的过程,但会起作用。

      <router-link to="#scroll" @click.native="scrollFix('#scroll')">Scroll</router-link>
      

      您还可以使用路由器的 afterEach 方法做一些事情,但还没有弄清楚。

      【讨论】:

      • 这太棒了!我希望有一种方法可以将点击事件分配给路由器本身。我试过beforeEach,但它似乎没有用书签触发。
      • @Alex 可能值得在 github repo 上添加一个问题,看看是否有更好的方法,我几乎可以肯定有。
      • Vue.js forum 上提出了同样的问题。我相信在 Github 上,他们倾向于将您推荐回 StackOverflow 和论坛,除非它是某种错误报告 :)
      • 这是你作为一个班轮的答案:mounted(){ setTimeout(() =&gt; location.href = this.$route.hash, 1); },
      • @RoccoB 好建议。我编辑了答案以添加箭头功能,但保留了该方法,因此在挂载的钩子和单击事件中它是相同的。
      【解决方案3】:

      如果您已经在使用哈希的路线上,您可以将其设置为滚动到目标。

      (另请注意,如果您已经在尝试前往的路线上,则不会调用路由器中的 scrollBehavior() 方法)。

      export default {
        methods: {
          anchorHashCheck() {
            if (window.location.hash === this.$route.hash) {
              const el = document.getElementById(this.$route.hash.slice(1))
              if (el) {
                window.scrollTo(0, el.offsetTop)
              }
            }
          },
        },
        mounted() {
          this.anchorHashCheck()
        },
      }
      

      然后在你的&lt;router-link&gt;中添加一个@click.native来监听锚点上的事件,

      <router-link :to="{hash: '#some-link'}" @click.native="anchorHashCheck">
        Some link
      </router-link>
      

      【讨论】:

        【解决方案4】:

        我使用了这个解决方案:

        <router-link to="/about" @click.native="scrollFix('#team')" >The team</router-link>
        

        还有这个:

        methods: {
            scrollFix: function(hash) {
              setTimeout(() => $('html, body').animate({
              scrollTop: $(hash).offset().top
              }, 1000), 1)
            }
          }
        

        【讨论】:

        • 真的有理由将 jquery 添加到 vue 项目吗? :)
        【解决方案5】:

        IMO 更可重复使用的可能解决方案:

        this.$router.push({ name: 'home' }, undefined, () => { location.href = this.$route.hash })
        

        由于第三个参数是 abort() 函数,但它可能会产生不必要的副作用..

        如果你想全局使用它,给你的路由器添加一个函数:

        pushWithAnchor: function (routeName, toHash) {
            const fromHash = Router.history.current.hash
            fromHash !== toHash || !fromHash
            ? Router.push({ name: routeName, hash: toHash })
            : Router.push({ name: routeName, hash: fromHash }, undefined, () => { window.location.href = toHash })
          }
        

        并在组件中使用它:

        this.$router.options.pushWithAnchor('home', '#fee-calculator-section')
        

        在模板中,您可以执行以下操作:

        <a @click="this.$router.options.pushWithAnchor('home', '#fee-calculator-section')"></a>
        

        遗憾的是你不能使用滚动偏移

        【讨论】:

          猜你喜欢
          • 2017-03-13
          • 1970-01-01
          • 1970-01-01
          • 2019-03-11
          • 2020-09-28
          • 2018-11-11
          • 2017-03-09
          • 1970-01-01
          相关资源
          最近更新 更多