router-guard.js 7.9 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1 2 3 4 5 6 7 8 9 10
import {
  callPageHook
} from '../util'

function addKeepAliveInclude (componentName) {
  if (this.keepAliveInclude.indexOf(componentName) === -1) { // 目标页面,自动 include
    this.keepAliveInclude.push(componentName)
  }
}

fxy060608's avatar
fxy060608 已提交
11 12 13 14 15 16 17 18 19 20 21 22
let deltaIds = []

function removeKeepAliveInclude (componentNameOrDelta) {
  if (typeof componentNameOrDelta === 'number') {
    deltaIds = this.keepAliveInclude.splice(-(componentNameOrDelta - 1)).map(name => {
      return parseInt(name.split('-').pop())
    })
  } else {
    const index = this.keepAliveInclude.indexOf(componentNameOrDelta)
    if (index !== -1) {
      this.keepAliveInclude.splice(index, 1)
    }
fxy060608's avatar
fxy060608 已提交
23 24 25
  }
}

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
let positionStore = Object.create(null)

export function getTabBarScrollPosition (id) {
  return positionStore[id]
}

function saveTabBarScrollPosition (id) {
  positionStore[id] = {
    x: window.pageXOffset,
    y: window.pageYOffset
  }
}

function switchTab (routes, to, from) {
  if (
    to &&
42 43 44
    from &&
    to.meta.isTabBar &&
    from.meta.isTabBar
45 46 47
  ) { // tabbar 跳 tabbar
    saveTabBarScrollPosition(from.params.__id__)
  }
fxy060608's avatar
fxy060608 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
  // 关闭非 tabBar 页面
  const pages = getCurrentPages()
  for (let i = pages.length - 1; i >= 0; i--) {
    const pageVm = pages[i]
    const meta = pageVm.$page.meta
    if (!meta.isTabBar) {
      removeKeepAliveInclude.call(this, meta.name + '-' + pageVm.$page.id)
      callPageHook(pageVm, 'onUnload')
    }
  }
}

function reLaunch (toName) {
  __uniConfig.reLaunch = (__uniConfig.reLaunch || 1) + 1
  // 关闭所有页面
63
  const pages = getCurrentPages(true)
fxy060608's avatar
fxy060608 已提交
64
  for (let i = pages.length - 1; i >= 0; i--) {
65 66
    callPageHook(pages[i], 'onUnload')
    // 重新reLaunch至首页可能会被keepAlive,先手动强制destroy
67
    pages[i].$destroy()
fxy060608's avatar
fxy060608 已提交
68
  }
69 70
  this.keepAliveInclude = []
  // 清空 positionStore
71
  positionStore = Object.create(null)
fxy060608's avatar
fxy060608 已提交
72 73
}

74 75
let currentPages = []

fxy060608's avatar
fxy060608 已提交
76
function beforeEach (to, from, next, routes) {
77
  currentPages = getCurrentPages(true) // 每次 beforeEach 时获取当前currentPages,因为 afterEach 之后,获取不到上一个 page 了,导致无法调用 onUnload
fxy060608's avatar
fxy060608 已提交
78 79
  const fromId = from.params.__id__
  const toId = to.params.__id__
80
  const toName = to.meta.name + '-' + toId
fxy060608's avatar
fxy060608 已提交
81
  if (toId === fromId) { // 相同页面阻止
82 83 84 85 86 87 88
    // 处理外部修改 history 导致卡在当前页面的问题
    if (to.fullPath !== from.fullPath) {
      removeKeepAliveInclude.call(this, toName)
      next()
    } else {
      next(false)
    }
fxy060608's avatar
fxy060608 已提交
89 90 91 92 93 94 95 96 97 98 99 100 101 102
  } else if (to.meta.id && to.meta.id !== toId) { // id 不妥,replace跳转
    next({
      path: to.path,
      replace: true
    })
  } else {
    const fromName = from.meta.name + '-' + fromId

    switch (to.type) {
      case 'navigateTo':
        break
      case 'redirectTo':
        // 关闭前一个页面
        removeKeepAliveInclude.call(this, fromName)
103 104
        if (from.meta) {
          if (from.meta.isQuit) { // 如果 redirectTo 的前一个页面是 quit 类型,则新打开的页面也是 quit
105
            to.meta.isQuit = true
106 107 108 109 110 111 112 113 114 115
            to.meta.isEntry = !!from.meta.isEntry
          }
          if (from.meta.isTabBar) { // 如果是 tabBar,需要更新系统组件 tabBar 内的 list 数据
            to.meta.isTabBar = true
            to.meta.tabBarIndex = from.meta.tabBarIndex
            const appVm = getApp().$children[0]
            appVm.$set(appVm.tabBar.list[to.meta.tabBarIndex], 'pagePath', to.meta.pagePath)
          }
        }

fxy060608's avatar
fxy060608 已提交
116 117
        break
      case 'switchTab':
118
        switchTab.call(this, routes, to, from)
fxy060608's avatar
fxy060608 已提交
119 120 121
        break
      case 'reLaunch':
        reLaunch.call(this, toName)
122
        to.meta.isQuit = true // reLaunch后,该页面为 quit 类型
fxy060608's avatar
fxy060608 已提交
123 124 125 126 127
        break
      default:
        // 后退或非 API 访问
        if (fromId && fromId > toId) { // back
          removeKeepAliveInclude.call(this, fromName)
fxy060608's avatar
fxy060608 已提交
128 129 130
          if (this.$router._$delta > 1) {
            removeKeepAliveInclude.call(this, this.$router._$delta)
          }
fxy060608's avatar
fxy060608 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
        }
        break
    }

    if (to.type !== 'reLaunch' && from.meta.id) { // 如果不是 reLaunch,且 meta 指定了 id
      addKeepAliveInclude.call(this, fromName)
    }
    // if (to.type !== 'reLaunch') { // TODO 如果 reLaunch,1.keepAlive的话,无法触发页面生命周期,并刷新页面,2.不 keepAlive 的话,页面状态无法再次保留,且 routeView 的 cache 有问题
    addKeepAliveInclude.call(this, toName)
    // }
    if (process.env.NODE_ENV !== 'production') {
      console.debug(`Core:keepAliveInclude=${JSON.stringify(this.keepAliveInclude)}`)
    }
    /* eslint-disable no-undef */
    if (__PLATFORM__ === 'h5') {
      if (to.meta && to.meta.name) {
fxy060608's avatar
fxy060608 已提交
147 148 149 150 151 152 153 154
        document.body.className = 'uni-body ' + to.meta.name
        const nvueDirKey = 'nvue-dir-' + __uniConfig.nvue['flex-direction']
        if (to.meta.isNVue) {
          document.body.setAttribute('nvue', '')
          document.body.setAttribute(nvueDirKey, '')
        } else {
          document.body.removeAttribute('nvue')
          document.body.removeAttribute(nvueDirKey)
fxy060608's avatar
fxy060608 已提交
155
        }
fxy060608's avatar
fxy060608 已提交
156 157 158 159 160 161 162 163 164 165 166
      }
    }

    next()
  }
}

function afterEach (to, from) {
  const fromId = from.params.__id__
  const toId = to.params.__id__

167
  const fromVm = currentPages.find(pageVm => pageVm.$page.id === fromId) // 使用 beforeEach 时的 pages
fxy060608's avatar
fxy060608 已提交
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

  switch (to.type) {
    case 'navigateTo': // 前一个页面触发 onHide
      fromVm && callPageHook(fromVm, 'onHide')
      break
    case 'redirectTo': // 前一个页面触发 onUnload
      fromVm && callPageHook(fromVm, 'onUnload')
      break
    case 'switchTab':
      if (from.meta.isTabBar) { // 前一个页面是 tabBar 触发 onHide,非 tabBar 页面在 beforeEach 中已触发 onUnload
        fromVm && callPageHook(fromVm, 'onHide')
      }
      break
    case 'reLaunch':
      break
    default:
      if (fromId && fromId > toId) { // history back
        fromVm && callPageHook(fromVm, 'onUnload')
fxy060608's avatar
fxy060608 已提交
186 187 188 189 190 191
        if (this.$router._$delta > 1) {
          deltaIds.reverse().forEach(deltaId => {
            const pageVm = currentPages.find(pageVm => pageVm.$page.id === deltaId)
            pageVm && callPageHook(pageVm, 'onUnload')
          })
        }
fxy060608's avatar
fxy060608 已提交
192 193 194
      }
      break
  }
fxy060608's avatar
fxy060608 已提交
195 196 197 198

  delete this.$router._$delta
  deltaIds.length = 0

fxy060608's avatar
fxy060608 已提交
199 200
  if (to.type !== 'reLaunch') { // 因为 reLaunch 会重置 id,故不触发 onShow,switchTab 在 beforeRouteEnter 中触发
    // 直接获取所有 pages,getCurrentPages 正常情况下仅返回页面栈内,传 true 则返回所有已存在(主要是 tabBar 页面)
201
    const toVm = getCurrentPages(true).find(pageVm => pageVm.$page.id === toId) // 使用最新的 pages
fxy060608's avatar
fxy060608 已提交
202
    if (toVm) { // 目标页面若已存在,则触发 onShow
X
xiaoyucoding 已提交
203
      // 延迟执行 onShow,防止与 UniServiceJSBridge.emit('onHidePopup') 冲突。
fxy060608's avatar
fxy060608 已提交
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
      setTimeout(function () {
        if (__PLATFORM__ === 'h5') {
          const navigationBar = toVm.$parent.$parent.navigationBar
          if (typeof qh !== 'undefined') {
            qh.setNavigationBarTitle({
              title: document.title
            })
            qh.setNavigationBarColor({
              backgroundColor: navigationBar.backgroundColor
            })
            qh.setNavigationBarTextStyle({
              textStyle: navigationBar.textColor === '#000' ? 'black' : 'white'
            })
          }
        }
X
xiaoyucoding 已提交
219 220
        callPageHook(toVm, 'onShow')
      }, 0)
221 222 223
      if (__PLATFORM__ === 'h5') {
        document.title = toVm.$parent.$parent.navigationBar.titleText
      }
fxy060608's avatar
fxy060608 已提交
224 225 226 227 228 229 230 231 232 233 234 235
    }
  }
}
export default function initRouterGuard (appVm, routes) {
  // 处理keepAliveInclude
  appVm.$router.beforeEach(function (to, from, next) {
    beforeEach.call(appVm, to, from, next, routes)
  })
  // 处理前进时的 onUnload,onHide 和后退时的 onShow
  appVm.$router.afterEach(function (to, from) {
    afterEach.call(appVm, to, from)
  })
fxy060608's avatar
fxy060608 已提交
236
}