检测内部/部分组件内的路由变化(Nuxt 2.14 中的 VueRoute 3)

问题描述 投票:0回答:1

环境

有一个遗留的

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,页面更改时会发生以下情况:

enter image description here

验证码在转换过程中在页面上“跳跃”。因此,我想,在验证码

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 });
    }
  },
});

这可行,但是......感觉很奇怪且不可靠。还有其他想法,包括:

  1. 使用Vue内置

    @leave
    的事件
    transition
    (例如https://jsfiddle.net/wy9mu0cf),但似乎事件传递得不够深入,组件
    InnerPartialComponent
    无法捕获它.
    相关 answer在 DOM 更改之前更改路由且在路由本身之外时触发事件?...)。

  2. $route
    内观看
    InnerPartialComponent
    ,但它不起作用,因为在页面更改时
    InnerPartialComponent
    被“销毁”时,观察者在实际处理更改之前就被删除了。
    相关答案Vuejs:路线更改事件...)。

问题

希望这个项目能够达到升级点,但在那一刻之前,什么是替代解决方法并捕获路线更改以在过渡期间隐藏验证码的适当选项?

尽管路线改变技术仍然很有趣,但什么会导致验证码“跳跃”呢?似乎没有自定义 CSS 属性可以替代 Vuetify 的样式,从而对验证码位置进行如此大的修改。

并且...非常感谢您提前提出建议! ✨

javascript vue.js nuxt.js vuetify.js vue-router
1个回答
0
投票

在查看 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...)

© www.soinside.com 2019 - 2024. All rights reserved.