有一个遗留的
nuxt
(v2.14.6
) 项目,包含 vue-route
(v3.4.3
)、模块 nuxt-i18n
(v6.15.1
) 和 vuetify
(v2.7.2
)。确实...这里有一些历史!
正在通过push完成页面更改:
<!-- File: ./pages/user/_slug/index.vue -->
<script lang="ts">
import mixins from 'vue-typed-mixins';
import SomeMixin from '~/mixins/SomeMixin';
export default mixins(SomeMixin).extend({
name: 'UserPage',
// ...
methods: {
async submit(): Promise<void> {
const response = await this.$api(/* ... */) // ...
// ...
this.$router.push(
this.localePath({
name: 'user-slug-management',
params: {
slug: String(this.user.slug ?? this.user.id)
}
})
);
}
// ...
}
// ...
});
</script>
另一个页面的路径名在
head
:
<!-- File: ./pages/user/_slug/management.vue -->
<template>
<div>
<SomeComponent />
</div>
</template>
<script lang="ts">
import mixins from 'vue-typed-mixins';
import SomeMixin from '~/mixins/SomeMixin';
import config from '~/utils/config';
import SomeComponent from '~/components/SomeComponent.vue'
export default mixins(SomeMixin).extend({
transition: 'scroll-x-reverse-transition',
name: 'UserManagement',
components: {
SomeComponent
},
head(): any {
const canonicalPathOptions = {
name: 'user-slug-management',
params: {
slug: String(this.user.slug ?? this.user.id)
}
}
return {
title: this.$t('this.title'),
meta: [{ hid: 'robots', name: 'robots', content: 'noindex' }],
link: [
{
rel: 'canonical',
href: config.BASE_URL + this.localePath(canonicalPathOptions),
hid: 'canonical'
},
{
rel: 'alternate',
hreflang: 'en',
content: config.BASE_URL + this.localePath(canonicalPathOptions, 'en')
},
{
rel: 'alternate',
hreflang: 'lt',
content: config.BASE_URL + this.localePath(canonicalPathOptions, 'lt')
},
// ...
]
}
}
});
Vue 组件
PartialComponent
有另一个 InnerPartialComponent
,而后者内部有一个 Google Recaptcha(即 VueRecaptcha
),实际上是一个旧的 vue-recaptcha
(v1.3.0
)。所以,总的来说:
management.vue
> SomeComponent
>> PartialComponent
>>> InnerPartialComponent
>>>> VueRecaptcha
由于来自
Vuetify的
scroll-x-reverse-transition
的 Vue 路由转换 management.vue
,页面更改时会发生以下情况:
验证码在转换过程中在页面上“跳跃”。因此,我想,在验证码
v-show
组件上使用 v-if
或 VueRecaptcha
来隐藏路线更改期间的元素是合适的,但我仍在寻找适当的方法。
目前,有点奇怪的解决方法是在“路由组件”中的beforeRouteLeave
中调度一个CustomEvent,并更改内部有
InnerPartialComponent
的VueRecaptcha
的道具来控制其可见性,例如<InnerPartialComponent :is-captcha-visible="isCaptchaVisible" />
和然后<VueRecaptcha v-show="isCaptchaVisible" />
里面。
<!-- File: ./mixins/SomeMixin.ts -->
import Vue from 'vue'
export default Vue.extend({
layout: 'main',
transition: 'scroll-x-transition',
// ...
beforeRouteLeave(to, from, next) {
window.dispatchEvent(new CustomEvent('before-route-leave', {
detail: { to, from }
}));
next();
}
});
<!-- File: ./components/PartialComponent.vue -->
<template>
<div>
<InnerPartialComponent v-if="isCaptchaVisible" ref="recaptcha" :site-key="reCaptchaSiteKey" />
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import InnerPartialComponent from '~/components/InnerPartialComponent.vue';
import config from '~/utils/config';
export default Vue.extend({
name: 'PartialComponent',
components: {
InnerPartialComponent
},
data() {
return {
reCaptchaSiteKey: '' as string
}
},
computed: {
isCaptchaVisible(): boolean {
return (this.reCaptchaSiteKey as string).length > 0
}
},
created() {
if (process.client) {
this.reCaptchaSiteKey = config.RECAPTCHA_SITE_KEY as string;
window.addEventListener('before-route-leave', () => {
if (process.client) {
this.reCaptchaSiteKey = '';
}
}, { once: true });
}
},
});
这可行,但是......感觉很奇怪且不可靠。还有其他想法,包括:
使用Vue内置
@leave
的事件transition
(例如https://jsfiddle.net/wy9mu0cf),但似乎事件传递得不够深入,组件InnerPartialComponent
无法捕获它.在
$route
内观看 InnerPartialComponent
,但它不起作用,因为在页面更改时 InnerPartialComponent
被“销毁”时,观察者在实际处理更改之前就被删除了。希望这个项目能够达到升级点,但在那一刻之前,什么是替代解决方法并捕获路线更改以在过渡期间隐藏验证码的适当选项?
尽管路线改变技术仍然很有趣,但什么会导致验证码“跳跃”呢?似乎没有自定义 CSS 属性可以替代 Vuetify 的样式,从而对验证码位置进行如此大的修改。
并且...非常感谢您提前提出建议! ✨
在查看 Vue Route 的源代码时,注意到
beforeEach
显然是全局可用的:
// ...
export default class VueRouter {
// ...
beforeHooks: Array<?NavigationGuard>
// ...
beforeEach (fn: Function): Function {
return registerHook(this.beforeHooks, fn)
}
// ...
}
// ...
function registerHook (list: Array<any>, fn: Function): Function {
list.push(fn)
return () => {
const i = list.indexOf(fn)
if (i > -1) list.splice(i, 1)
}
}
router.beforeEach((to, from, next) => { /* must call `next` */ }) router.beforeResolve((to, from, next) => { /* must call `next` */ }) router.afterEach((to, from) => {})
所有三个方法都返回一个删除已注册的防护/挂钩的函数。
我仍然不确定这是否是解决 ReCaptcha 重新定位问题的最合适的解决方案,但这种钩子用法感觉确实更适合处理路由路径更改上的内部/非路由组件的可见性。
<!-- File: ./components/PartialComponent.vue -->
<template>
<div>
<InnerPartialComponent v-if="isCaptchaVisible" ref="recaptcha" :site-key="reCaptchaSiteKey" />
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import InnerPartialComponent from '~/components/InnerPartialComponent.vue';
import config from '~/utils/config';
export default Vue.extend({
name: 'PartialComponent',
components: {
InnerPartialComponent
},
data() {
return {
reCaptchaSiteKey: '',
registeredHooks: []
}
},
computed: {
isCaptchaVisible(): boolean {
return this.reCaptchaSiteKey.length > 0
}
},
created() {
if (process.client) {
this.reCaptchaSiteKey = config.RECAPTCHA_SITE_KEY as string;
this.registeredHooks.push(this.$router.beforeEach((to, from, next) => {
this.reCaptchaSiteKey = '';
next();
}));
}
},
destroyed() {
while (this.registeredHooks.length) {
this.registeredHooks.shift()();
}
}
});
- vuejs/vue-router/issues/1106(删除 beforeEach 钩子...)
- vuejs/vue-router/issues/2341(删除所有 beforeEach、afterEach、beforeResolve...)
- vuejs/vue-router/issues/938(守卫建议:beforeRouteUpdate...)