如何使用 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】:我在资源中没有找到任何可以解决您的问题的内容,但您可以在包含您的<router-view></router-view>
的组件的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
修饰符并绑定到您的<router-link></router-link>
。这是一个相当手动的过程,但会起作用。
<router-link to="#scroll" @click.native="scrollFix('#scroll')">Scroll</router-link>
您还可以使用路由器的 afterEach
方法做一些事情,但还没有弄清楚。
【讨论】:
这太棒了!我希望有一种方法可以将点击事件分配给路由器本身。我试过beforeEach
,但它似乎没有用书签触发。
@Alex 可能值得在 github repo 上添加一个问题,看看是否有更好的方法,我几乎可以肯定有。
在Vue.js forum 上提出了同样的问题。我相信在 Github 上,他们倾向于将您推荐回 *** 和论坛,除非它是某种错误报告 :)
这是你作为一个班轮的答案:mounted() setTimeout(() => 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()
,
然后在你的<router-link>
中添加一个@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 处理锚点(书签)?的主要内容,如果未能解决你的问题,请参考以下文章