如何使用 Vue Router 处理锚点(书签)?

Posted

技术标签:

【中文标题】如何使用 Vue Router 处理锚点(书签)?【英文标题】:How to handle anchors (bookmarks) with 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 
        
    
)

【问题讨论】:

【参考方案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 上,他们倾向于将您推荐回 *** 和论坛,除非它是某种错误报告 :) 这是你作为一个班轮的答案: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>

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

【讨论】:

以上是关于如何使用 Vue Router 处理锚点(书签)?的主要内容,如果未能解决你的问题,请参考以下文章

vue使用----------vue-router路由的使用

Vue路由scrollBehavior滚动行为控制锚点

Vue Router-更改滚动中的锚点

Vue-router路由系统介绍

vue中的坑 --- 锚点与查询字符

Vue路由器导航或滚动到锚点(#锚点)