<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      優化用戶體驗:攔截瀏覽器前進后退、刷新、關閉、路由跳轉等用戶行為并彈窗提示

      ????? 寫在開頭

      點贊 + 收藏 === 學會??????

      需求

      首先列舉一下需要攔截的行為,接下來我們逐個實現。

      1. 瀏覽器前進后退
      2. 標簽頁刷新和關閉
      3. 路由跳轉

      1、攔截瀏覽器前進后退

      這里的實現是核心,涉及到大量 History API 的理解,如果不太了解可以先看一下這兩個文章:
      攔截瀏覽器后退方法附帶獨家干貨知識點
      瀏覽器的History、Location對象,及使用js控制網頁的前進后退和加載,刷新當前頁面總結!

      首先給大家明確一點,出于安全問題,瀏覽器并不支持通過js攔截瀏覽器的前進后退操作,但是可以使用障眼法。
      具體思路就是我們可以在頁面加載的時候,使用 history.pushState 這個API給頁面添加一個當前頁面的歷史記錄(不會導致頁面刷新),此時最近的兩條歷史記錄都是當前頁面,當用戶點擊后退的時候,瀏覽器會退到上一個記錄(還是當前頁面),這時會觸發 popstate事件 ,回退的時候再往歷史記錄里添加一條當前頁面的記錄(為了下次攔截使用),同時我們使用彈窗提示用戶一些信息,如果用戶確定要回退,我們再使用 history.go(-2) 跳過這兩條當前頁面的記錄,返回到真正的上個頁面,這樣我們就成功模擬了回退操作的攔截。

      代碼實現:

      import { onUnmounted } from 'vue'
      
      interface IBrowserInterceptEvents {
        popstate?: (next: () => void) => void // 監聽瀏覽器前進后退
      }
      
      // 作用:添加一個歷史記錄,以便后續模擬攔截后退
      function addStopHistory() {
        const state = { id: 'stopBack' }
        if (history.state.id === 'stopBack') return
        history.pushState(state, '', window.location.href)
      }
      
      const useBrowserInterceptor = (events: IBrowserInterceptEvents) => {
        const { popstate } = events
        let popstateCallback: EventListener | undefined
      
        let isHistoryBack = false
        // 攔截瀏覽器后退
        if (popstate) {
          addStopHistory()
          popstateCallback = () => {
            addStopHistory()
            popstate(() => {
              isHistoryBack = true
              history.go(-2)
            })
          }
          window.addEventListener('popstate', popstateCallback)
        }
      
        // 銷毀事件
        onUnmounted(() => {
          // 不是歷史后退觸發的,僅僅是組件卸載,才需要清除模擬攔截后退時添加的歷史記錄
          if (popstate && !isHistoryBack) {
            history.go(-1)
          }
          popstateCallback && window.removeEventListener('popstate', popstateCallback)
        })
      }
      
      export default useBrowserInterceptor

      使用

      // 使用攔截
      useBrowserInterceptor({
        popstate: showWarnModal,
      })
      
      // 彈窗提示
      const showWarnModal = (next: any) => {
        const { pending, uploading, failed } = taskStatusMap.value
        if (pending + uploading + failed > 0) {
          Modal.confirm({
            title: h('h3', '當前頁面有未完成的任務!'),
            width: 500,
            content: h('div', null, [
              taskStatusMap.value.pending
                ? h(Tag, { color: 'default' }, `待上傳:${taskStatusMap.value.pending}`)
                : null,
              taskStatusMap.value.uploading
                ? h(Tag, { color: 'processing' }, `上傳中:${taskStatusMap.value.uploading}`)
                : null,
              taskStatusMap.value.failed
                ? h(Tag, { color: 'error' }, `上傳失敗:${taskStatusMap.value.failed}`)
                : null,
              h(
                'div',
                { style: { marginTop: '10px' } },
                '此操作會導致未完成上傳的視頻數據丟失,確定要繼續嗎?'
              )
            ]),
            onOk() {
              next()
            }
          })
        } else {
          next()
        }
      }

      2、攔截標簽頁刷新和關閉

      這個比較簡單,我們只需要監聽 beforeunload 事件,阻止默認行為即可。但是這里要注意:出于瀏覽器安全問題,我們只能使用瀏覽器默認彈窗提示(如下圖),無法自定義提示內容。

      歷史回退也有可能導致觸發 beforeunload 事件,所以要添加一個 isHistoryBack 變量做判斷區分。

      刷新頁面:

       關閉頁面:

       

      代碼實現

      import { onUnmounted } from 'vue'
      
      interface IBrowserInterceptEvents {
        popstate?: (next: () => void) => void // 監聽瀏覽器前進后退
        beforeunload?: EventListener // 監聽標簽頁刷新和關閉
      }
      
      // addStopHistory ...
      
      const useBrowserInterceptor = (events: IBrowserInterceptEvents) => {
        const { popstate, beforeunload } = events
        let popstateCallback: EventListener | undefined
        let beforeunloadCallback: EventListener | undefined
      
        let isHistoryBack = false
        // 攔截瀏覽器后退 ...
      
        // 攔截標簽頁關閉和刷新
        if (beforeunload) {
          beforeunloadCallback = (event) => {
            if (!isHistoryBack) beforeunload(event)
          }
          window.addEventListener('beforeunload', beforeunloadCallback)
        }
      
        // 銷毀事件
        onUnmounted(() => {
          // 不是后退且不是導航守衛觸發的,僅僅是組件卸載,才需要清除模擬攔截后退時添加的歷史記錄
          if (popstate && !isHistoryBack) {
            history.go(-1)
          }
          popstateCallback && window.removeEventListener('popstate', popstateCallback)
          beforeunloadCallback && window.removeEventListener('beforeunload', beforeunloadCallback)
        })
      }
      
      export default useBrowserInterceptor

      使用

      useBrowserInterceptor({
        popstate: showWarnModal,
        beforeunload: (e) => {
          const { pending, uploading, failed } = taskStatusMap.value
          if (pending + uploading + failed > 0) {
            e.preventDefault()
            e.returnValue = false
          }
        }
      })

      3、攔截路由跳轉(完整版)

      這里我們可以使用 vue-router 提供的 onBeforeRouteLeave 鉤子函數在組件內注冊一個導航守衛,當用戶跳轉路由的時候進行彈窗提示。

      歷史回退也有可能觸發導航守衛,也要使用 isHistoryBack 做判斷區分。

      最后我們還要處理一下事件的銷毀,組件卸載時銷毀事件,這里有個注意點:我們不僅要移除注冊的事件,當組件卸載不是歷史后退(isHistoryBack)也不是路由跳轉(isRouter)觸發的,僅僅是組件卸載(比如v-if),這個時候還需要清除模擬攔截后退時添加的歷史記錄,否則會造成頁面回退異常。

       

      代碼實現(完整版)

      import { onUnmounted } from 'vue'
      import { type NavigationGuardNext, onBeforeRouteLeave } from 'vue-router'
      
      interface IBrowserInterceptEvents {
        popstate?: (next: () => void) => void // 監聽瀏覽器前進后退
        beforeunload?: EventListener // 監聽標簽頁刷新和關閉
        beforeRouteLeave?: (next: NavigationGuardNext) => void // 導航守衛
      }
      
      // 作用:添加一個歷史記錄,以便后續模擬攔截后退
      function addStopHistory() {
        const state = { id: 'stopBack' }
        if (history.state.id === 'stopBack') return
        history.pushState(state, '', window.location.href)
      }
      
      const useBrowserInterceptor = (events: IBrowserInterceptEvents) => {
        const { popstate, beforeunload, beforeRouteLeave } = events
        let popstateCallback: EventListener | undefined
        let beforeunloadCallback: EventListener | undefined
      
        let isHistoryBack = false
        let isRouter = false
        // 攔截瀏覽器后退
        if (popstate) {
          addStopHistory()
          popstateCallback = () => {
            addStopHistory()
            popstate(() => {
              isHistoryBack = true
              history.go(-2)
            })
          }
          window.addEventListener('popstate', popstateCallback)
        }
      
        // 攔截標簽頁關閉和刷新
        if (beforeunload) {
          beforeunloadCallback = (event) => {
            if (!isHistoryBack) beforeunload(event)
          }
          window.addEventListener('beforeunload', beforeunloadCallback)
        }
      
        // 導航守衛
        beforeRouteLeave &&
          onBeforeRouteLeave((_to, _from, next) => {
            if (isHistoryBack) {
              next()
              return
            }
            beforeRouteLeave(() => {
              isRouter = true
              next()
            })
          })
      
        // 銷毀事件
        onUnmounted(() => {
          // 不是后退且不是導航守衛觸發的,僅僅是組件卸載,才需要清除模擬攔截后退時添加的歷史記錄
          if (popstate && !isHistoryBack && !isRouter) {
            history.go(-1)
          }
          popstateCallback && window.removeEventListener('popstate', popstateCallback)
          beforeunloadCallback && window.removeEventListener('beforeunload', beforeunloadCallback)
        })
      }
      
      export default useBrowserInterceptor

      使用

      // 使用攔截
      useBrowserInterceptor({
        beforeRouteLeave: showWarnModal,
        popstate: showWarnModal,
        beforeunload: (e) => {
          const { pending, uploading, failed } = taskStatusMap.value
          if (pending + uploading + failed > 0) {
            e.preventDefault()
            e.returnValue = false
          }
        }
      })
      
      // 彈窗提示
      const showWarnModal = (next: any) => {
        const { pending, uploading, failed } = taskStatusMap.value
        if (pending + uploading + failed > 0) {
          Modal.confirm({
            title: h('h3', '當前頁面有未完成的任務!'),
            width: 500,
            content: h('div', null, [
              taskStatusMap.value.pending
                ? h(Tag, { color: 'default' }, `待上傳:${taskStatusMap.value.pending}`)
                : null,
              taskStatusMap.value.uploading
                ? h(Tag, { color: 'processing' }, `上傳中:${taskStatusMap.value.uploading}`)
                : null,
              taskStatusMap.value.failed
                ? h(Tag, { color: 'error' }, `上傳失敗:${taskStatusMap.value.failed}`)
                : null,
              h(
                'div',
                { style: { marginTop: '10px' } },
                '此操作會導致未完成上傳的視頻數據丟失,確定要繼續嗎?'
              )
            ]),
            onOk() {
              next()
            }
          })
        } else {
          next()
        }
      }

      總結

      我們實現了對 用戶刷新、關閉標簽頁、瀏覽器歷史回退、路由跳轉 等操作的攔截,可以在某些特殊場景下給用戶一些友好的提示,提升用戶體驗。

      如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

      posted @ 2025-05-22 16:23  林恒  閱讀(491)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 色婷婷五月综合久久| 内射视频福利在线观看| 中文字幕乱妇无码AV在线| 日韩AV高清在线看片| 国产精品人妻一区二区高| 色综合色综合色综合频道| 永嘉县| 青草成人精品视频在线看| 国产对白老熟女正在播放| 亚洲人午夜精品射精日韩| 国产不卡精品视频男人的天堂| 女人张开腿无遮无挡视频| 18禁无遮拦无码国产在线播放 | 国产成人午夜精品福利| 国产成人8X人网站视频| 苍井空一区二区三区在线观看| 久久精品亚洲日本波多野结衣| AV无码免费不卡在线观看 | 国产激情国产精品久久源| 邢台市| 亚洲国产超清无码专区| 欧美色丁香| 九九热视频在线免费观看| 亚洲国产制服丝袜高清在线| 中文字幕无码av波多野吉衣| 中文国产成人精品久久一| 洪泽县| 亚洲av无码成人精品区一区 | 人妻另类 专区 欧美 制服| 国产成人综合久久亚洲av| 久久a级片| 国产亚欧女人天堂AV在线| 国产精品护士| 人妻少妇偷人精品一区| 久久99热只有频精品8| 欧美成人精品在线| 久久综合给合久久狠狠狠| 欧洲无码一区二区三区在线观看| 新久久国产色av免费看| 午夜通通国产精品福利| 美日韩在线视频一区二区三区|