router-guard.js 7.1 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 147 148 149 150 151 152 153 154 155 156 157 158
        }
        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) {
        document.body.className = 'uni-body ' + to.meta.name
      }
    }

    next()
  }
}

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

159
  const fromVm = currentPages.find(pageVm => pageVm.$page.id === fromId) // 使用 beforeEach 时的 pages
fxy060608's avatar
fxy060608 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

  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 已提交
178 179 180 181 182 183
        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 已提交
184 185 186
      }
      break
  }
fxy060608's avatar
fxy060608 已提交
187 188 189 190

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

fxy060608's avatar
fxy060608 已提交
191 192
  if (to.type !== 'reLaunch') { // 因为 reLaunch 会重置 id,故不触发 onShow,switchTab 在 beforeRouteEnter 中触发
    // 直接获取所有 pages,getCurrentPages 正常情况下仅返回页面栈内,传 true 则返回所有已存在(主要是 tabBar 页面)
193
    const toVm = getCurrentPages(true).find(pageVm => pageVm.$page.id === toId) // 使用最新的 pages
fxy060608's avatar
fxy060608 已提交
194
    if (toVm) { // 目标页面若已存在,则触发 onShow
X
xiaoyucoding 已提交
195
      // 延迟执行 onShow,防止与 UniServiceJSBridge.emit('onHidePopup') 冲突。
196
      setTimeout(function () {
X
xiaoyucoding 已提交
197 198
        callPageHook(toVm, 'onShow')
      }, 0)
199 200 201
      if (__PLATFORM__ === 'h5') {
        document.title = toVm.$parent.$parent.navigationBar.titleText
      }
fxy060608's avatar
fxy060608 已提交
202 203 204 205 206 207 208 209 210 211 212 213
    }
  }
}
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)
  })
214
}