在混合式开发中,如果原生app不提供监听app回退事件的办法那么H5是不晓得app曾经回退了,但有时候咱们又须要去监听页面的回退,并做相应解决。

比方,在商品领取页面,输出领取明码是一个相似微信领取的弹窗,用户关上这个弹窗后筹备输出领取明码,但此时用户又不想领取了,此时用户就会触发app的返来敞开这个弹窗,如下视频:

如果H5不做监听并阻止的话那么用户的这个动作就变成了返回上一个页面了

1、实现原理

实现原理:监听history.state的变更,每次关上弹窗时向history.state增加一个新状态
实现步骤:

  1. 页面初始化后监听popstate事件,在该事件中进行业务解决

    let originState = history.state.current; // 存储原始的状态let onPopstate = function (e) {  console.log('监听到页面产生动作', e.state, originState);  if (e.state && (e.state.current === originState || e.state.target === originState)) { // 在这里能够进行业务解决,如敞开弹窗,提醒用户未解决的事件等  }};// 监听 popstate 事件window.addEventListener("popstate", onPopstate, false);

    留神:这里须要存储页面一进来时history.state的状态,当页面产生回退时能力晓得在以后页面产生了回退

  2. 关上弹窗时向hostory.state增加一个新状态

    let changeHistoryState = function () {  // 这里只须要再增加一个原始的状态即可,当发送回退时后面增加的`popstate`事件处理函数就能正确处理了  window.history.pushState({target: originState, random: Math.random()}, "", location.href);};
  3. H5被动敞开弹窗
    h5被动敞开弹窗时需调用window.history.back()回退到上一个状态,否则用户想返回上一个页面时须要触发两次app的回退能力回到上一个页面

2、代码实现

useListenerAppBack.js

import {  onUnmounted} from 'vue';/** * 监听原生app返回事件并阻止返回到上一个页面 */export function useListenerAppBack (onAppBack) {  let appBack = function () {    window.history.back();  };  let appForward = function () {    window.history.forward();  };  let appGo = function (delta) {    window.history.go(delta);  };  if (!window.history.pushState) {    return {      appBack,      appForward,      appGo,      changeHistoryState () {        console.warn('Browser does not support pushState!');      }    };  }  let originState = history.state.current; // 存储原始的状态  // let statePushed = false;  let changeHistoryState = function () {    // 这里只须要再增加一个原始的状态即可    window.history.pushState({target: originState, random: Math.random()}, "", location.href);  };  let onPopstate = function (e) {    console.log('监听到页面产生动作', e.state, originState);    if (e.state && (e.state.current === originState || e.state.target === originState)) {      onAppBack(e.state);    }  };  // 监听 popstate 事件  window.addEventListener("popstate", onPopstate, false);  onUnmounted(function () {    console.log('移除popstate事件监听');    // 移除监听 popstate 事件    window.removeEventListener("popstate", onPopstate, false);  });  return {    appBack,    appForward,    appGo,    changeHistoryState  };};

业务代码中应用,pay.vue:

<template>  <div class="pay">    <bs-button @click="openPayDialog" :loading="paying" :disabled="paying">立刻领取</bs-button>    <!--领取弹窗-->    <PayDialog      v-model:visible="payDialogData.visible"      @shadow-click="onPayDialogShadowClick"></PayDialog>  </div></template><script>import {  ref,  reactive} from 'vue';import { useListenerAppBack } from '@/hooks/useListenerAppBack';export default {  name: "Pay",  setup () {    let paying = ref(false);    let payDialogData = reactive({      visible: false    });    let { appBack, changeHistoryState } = useListenerAppBack(function () {      if (payDialogData.visible) {        // 如果领取弹窗曾经关上,且页面产生了回退,则将领取弹窗敞开        payDialogData.visible = false;      }    });    let openPayDialog = function () {      // 每次关上弹窗时都需扭转一下history.state的状态      changeHistoryState();      payDialogData.visible = true;    };    // 领取弹窗的半透明遮罩点击事件,点击半透明遮罩后应该敞开领取弹窗,并回退一下history.state    let onPayDialogShadowClick = function () {      payDialogData.visible = false;      appBack();    };    return {      paying,      payDialogData,      openPayDialog,      onPayDialogShadowClick    };  }};</script>

最终成果:
苹果手机成果

安卓手机成果

3、苹果手机中的坑

在苹果手机中领取弹窗不要增加侧滑成果,否则在回退敞开弹窗时会呈现页面闪动一下的成果,如下图:

侧滑成果代码:

.pay-dialog{  position: fixed;  top: 0;  left: 0;  bottom: 0;  right: 0;  z-index: 1000;  background-color: #fff;  &.slide-enter-from,  &.slide-leave-to{    transform: translateX(100%);  }  &.slide-enter-active,  &.slide-leave-active{    transition: transform .2s linear;  }  &.slide-enter-to,  &.slide-leave-from{    transform: translateX(0);  }}