如何使用 Vue Router 处理锚点(书签)?
How to handle anchors (bookmarks) with Vue Router?
我正在寻找一种使用 Vue Router 处理页内锚点的智能方法。考虑以下因素:
<router-link to="#app">Apply Now</router-link>
<!-- some HTML markup in between... -->
<div id="app">...</div>
"scroll to anchor" 行为 described in the docs 工作正常,除了:
- 当您单击锚点时,它会将您带到
div id="app"
。现在从 div
滚动回到锚点并尝试再次单击它 -- 这次您将 而不是 跳到 div
。事实上,锚点将保留 class 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 }
}
}
})
我没有在资源中找到任何解决您问题的方法,但您可以利用 mounted
挂钩中包含 <router-view></router-view>
的组件的 $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
方法来做一些事情,但还没有想出来。
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>
遗憾的是你不能使用滚动偏移
我使用了这个解决方案:
<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)
}
}
如果您已经在带有散列的路线上,您可以将其设置为滚动到目标。
(另请注意,如果您已经在尝试前往的路线上,则路由器中的 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>
我知道你问过锚点,你已经有了答案。但是,以下功能应该适用于锚点和常规链接。它允许您滚动到与路由匹配的组件的第一个实例的位置。我写它是为了看看我是否可以在保留锚式滚动的同时绕过使用哈希。
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
值为空。这不是最优雅的解决方案,但它可能会帮助其他人。
注意:这仅在您想要滚动组件的第一个实例时有效。
我正在寻找一种使用 Vue Router 处理页内锚点的智能方法。考虑以下因素:
<router-link to="#app">Apply Now</router-link>
<!-- some HTML markup in between... -->
<div id="app">...</div>
"scroll to anchor" 行为 described in the docs 工作正常,除了:
- 当您单击锚点时,它会将您带到
div id="app"
。现在从div
滚动回到锚点并尝试再次单击它 -- 这次您将 而不是 跳到div
。事实上,锚点将保留 classrouter-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 }
}
}
})
我没有在资源中找到任何解决您问题的方法,但您可以利用 mounted
挂钩中包含 <router-view></router-view>
的组件的 $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
方法来做一些事情,但还没有想出来。
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>
遗憾的是你不能使用滚动偏移
我使用了这个解决方案:
<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)
}
}
如果您已经在带有散列的路线上,您可以将其设置为滚动到目标。
(另请注意,如果您已经在尝试前往的路线上,则路由器中的 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>
我知道你问过锚点,你已经有了答案。但是,以下功能应该适用于锚点和常规链接。它允许您滚动到与路由匹配的组件的第一个实例的位置。我写它是为了看看我是否可以在保留锚式滚动的同时绕过使用哈希。
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
值为空。这不是最优雅的解决方案,但它可能会帮助其他人。
注意:这仅在您想要滚动组件的第一个实例时有效。