标记本次浏览的第一个页面

原创
2020/11/10 14:00
阅读数 96

开发hybrid应用时经常使用WebView来加载前端网页,并且现在SPA已是主流,那么路由也由前端控制,主要是使用history.pushStatehistory.replaceState方法。

为了进一步的让前端控制网页的内容,WebView会把原生的顶部导航栏隐藏掉,让前端自己渲染一个导航栏,并且实现返回的按钮。

一般来说,这个返回按钮需要两个功能:1. 路由的后退,2. 在第一层路由退出WebView。

通常退出WebView由客户端提供的桥实现,但是判断当前路由是否是第一层路由,即当前的页面是否是WebView刚打开时的路由,需要前端自行判断。

一个办法是在网页刚打开时,标记当前路由为一层路由。那么必须找一个可以保存与路由相关且不会刷新清楚的数据的地方。

history.state

history.pushStatehistory.replaceState方法可以改变路由并保存数据,从history.state可以读取保存的数据。这些数据的生命周期是伴随一次浏览,即浏览器打开到关闭的,与sessionStorage类似,它不会被刷新清除。

可以在网页刚进入时这样保存标记:

history.replaceState({ firstPage: true }, null)

在点击返回按钮时可以进行判断:

if (history.state.firstPage) {
    // 关闭WebView
} else {
    history.go(-1)
}

借由判断当前历史状态中的是否存在firstPageK来判断当前路由是否为第一层路由。

当在其它页面(非第一层路由)刷新时,这个操作可能会被重复执行,从而造成标记了两次的情况。为了解决这个问题,可以在sessionStorage中保存一个“第一层路由已标记”的标记,确保在整个浏览过程中标记操作不会被重复执行。示例:

let firstPageMark = sessionStorage.getItem('first-page-mark')
if (!firstPageMark) {
    history.replaceState({ firstPage: true }, null)
    sessionStorage.setItem('first-page-mark', '1')
}

实验

我们用Express来实验一下。

index.js

const Path = require('path')
const Express = require('express')

const app = Express()
const router = Express.Router()

router.get('/:number', (req, res, next) => {
  res.sendFile(Path.resolve(__dirname, './index.html'))
})

app.use(router)
app.listen(8080)

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="number"></div>
    <button id="back">Back</button>
    <button id="next">Next</button>

    <script>
      let number
      function setNumber() {
        number = +location.pathname.replace('/', '')

        document.querySelector('#number').innerText = number
      }
      setNumber()

      document.querySelector('#back').addEventListener('click', () => {
        if (history.state.firstPage) {
          console.log('First Page')
        } else {
          console.log('Back')

          history.go(-1)
        }

        setTimeout(() => {
          setNumber()
        })
      })
      document.querySelector('#next').addEventListener('click', () => {
        history.pushState({}, null, `/${++number}`)

        setNumber()
      })

      let firstPageMark = sessionStorage.getItem('first-page-mark')
      if (!firstPageMark) {
        history.replaceState({ firstPage: true }, null)
        sessionStorage.setItem('first-page-mark', '1')
      }
    </script>
  </body>
</html>

可以看到结果如下:

vue-router

vue-router为vue提供了前端路由的功能,其在底层也使用了history.pushStatehistory.replaceState方法。它在路由跳转的过程中会替换history.state,导致上述的方法失效。但是仍然可以使用上述方法的原理,因为vue-router会在history.state中为每一个路由保存一个全局唯一的key数据,只要记住第一层路由的key,就能通过它和当前页面的key比较来判断当前页面是不是第一层路由。

示例:

let firstPageKey = sessionStorage.getItem('first-page-key')
function setFirstPageKey(){
    if(!firstPageKey){
        firstPageKey = history.state.key
        sessionStorage.setItem('first-page-key', firstPageKey)
    }
}

setFirstPageKey方法必须在vue-router设置完key后调用,比如vue-router的afterEach导航守卫中。

在保存了第一层路由的key后,可以在返回按钮按下时这么判断:

if (history.state.key === firstPageKey) {
    // 关闭WebView
} else {
    router.back()
}

借由判断当前历史状态中的key是否与firstPageKey相同来判断当前路由是否为第一层路由。

刷新

与上一种方法不同的是,浏览器的刷新不会修改history.state,但是在使用的vue-router的页面中,刷新后vue-router会重新初始化,那么当前页面的key也会变更,使得原本记录的firstPageKey无效。而且为了防止重复记录,保存key这个操作被锁死,只能执行一次,所以firstPageKey无法被刷新。

如果刷新时的路由不是第一层路由,那么没有关系,因为这一层的key我们不关心。如果在第一层路由的页面刷新,那么在刷新前我们需要清楚sessionStorage中的first-page-key,一遍重新进入页面后,key能被重新保存。示例:

window.addEventListener('unload', () => {
  if (history.state.key === firstPageKey) {
    sessionStorage.set('first-page-set', '')
  }
})
展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部