使用 Vue/Nuxt 中的后退按钮关闭 Modal/Menu

Closing a Modal/Menu with the back button in Vue/Nuxt

我想在用户点击硬件后退按钮时关闭全屏菜单或模式。 我试过这个:

<script>
    export default {
        data(){
            return{
                isMenuOpen: false
            }
        },
        methods:{
            openMenu(){
                this.isMenuOpen = true
                document.addEventListener('backbutton', this.closeMenu, false)
            },
            closeMenu(){
                this.isMenuOpen = false
                document.removeEventListener('backbutton', this.closeMenu)
            }
        }
    }
</script>

但这不起作用。有人可以帮我吗?

假设,您可以将模式的状态(打开/关闭)与路由器的 属性 绑定,也许 your-route?isMenuOpen=[true/false]

并点击 [打开模式] 而不是

this.isMenuOpen = true 

做:

this.$router.push({path:'same-path', query: {isMenuOpen: true}});

并且模态绑定

<modal v-model="$router.query.isMenuOpen" />

我相信为 Vue 3 vue-router 合并的 RFC 之一包含对模态对话框的更好处理,但现在有一些替代方案可以正常工作。

there must be something to prevent the default behavior of the back button

这是通过推送新路由来完成的——您可以使用 中描述的查询参数或作为新路由来实现。我选择了新路由,效果还不错

安装 @nuxtjs/router-extras 并确保按照安装说明将其添加到您的 buildModules。然后,如果您的页面位于 pages/a.vue,则创建文件 pages/a/some-modal.vuesome-modal 是路径名):这将创建一个 子路由 。在这里,写下模态对话框的内容。在该组件中包括以下内容:

<router>
meta:
  showModal: true
</router>

现在,在 $route.meta 的父页面(将承载模态对话框的页面)中添加观察者。 Property decorator + TypeScript 语法:

export default class Index extends Vue {
    showModal = false;

    @Watch('$route.meta', { immediate: true })
    navigate(meta: { showModal?: boolean }) {
        this.showModal = !!meta.showModal;
    }

    back() {
        this.showModal = false;
        if (window.history.length > 2) {
            this.$router.back();
        } else {
            const pathPaths = this.$route.path.split('/');
            pathPaths.pop();
            this.$router.push(pathPaths.join('/'));
        }
    }
}

在您的模板中,包括:

<modal-component v-model="showModal" @close="back">
    <NuxtChild :extra-props="extraProps" />
</modal-component>

打开模态对话框:

<nuxt-link to="some-modal" append>Open</nuxt-link>

这不会保留查询参数。不过,创建自定义逻辑应该不会太难。

对于这种情况,我的代码与 Cosimo 的答案类似,但方法不同。

不同之处在于我保持由数据 属性 ( this.isMenuOpen )

触发的模态
data(){
  return {
    isMenuOpen: false
  }
}

所以你可以用它来绑定模态组件

<modal v-model="isMenuOpen" />

并且我添加了手表属性来观看查询

watch: {
  $route(newVal, oldVal) {
    this.isMenuOpen = newVal.query?.isMenuOpen || false
  }
}

然后在您的 openMenu 方法中您可以这样做

openMenu(){
  this.isMenuOpen = true
  this.$router.push({
    name : "same-path",
    query : {
      isMenuOpen : true
    }
  })
}

但是,在您的 closeMenu 中使用 $router.replace 而不是 $router.push

closeMenu(){
  this.isMenuOpen = false
  this.$router.replace({
    name : "same-path"
  })
}



为什么我只对 openModal 使用 $router.push ?因为它保存了状态的历史,所以无论何时打开模式,它都会告诉浏览器新的状态,然后你仍然可以使用后退按钮回到之前的状态。
按下后退按钮后,它将删除查询参数并触发手表 属性

为什么在 closeModal 中使用 $router.replace ?因为它只会替换状态,所以当你按下后退按钮时,它会返回到上一个 url 页面,而不是打开模态

简短回答:首先不要让模式看起来像页面

更长的答案:很好的问题,我遇到了同样的问题,试图 运行 通过答案,这是非常好的技巧。但是每次黑客攻击都会带来一个警告,在这种情况下的警告是历史状态仍然保留所有这些推送。因此,虽然您可以打开一个模式(路由器推送)并使用后退按钮将其关闭一次(使用路由器替换),但当您打开一个模式并多次关闭它时,您很快就会发现您必须按下后退按钮现在有更多次到达你想要的状态,这是因为历史状态现在有更多的状态。我试图找到一种方法来 'delete' 一个状态,但从我的发现来看这是不可能的(甚至 $router.go(-1) 有警告)

None 如果您的模态框作为模态框清晰可见(围绕它的透明背景),这就是我在平板电脑/桌面视图中的情况。在移动设备上,我试图厚颜无耻地设置一个与模式匹配的背景颜色,它看起来像一个漂亮的刷新新页面,即使它只是一个模式(与往常一样右上角的关闭 'x' 图标) .

我能想出的最佳解决方案是观察模态状态,如果打开,添加一个 beforeunload 函数,如果他们确定要离开,将提示用户。

'modal.isOpen': {
    immediate: true,
    handler: function(newVal) {
        if (newVal) {
            window.onbeforeunload = this.beforeWindowUnload;
        }
        else {
            window.onbeforeunload = null;
        }
    }
}