Skip to content

Commit

Permalink
perf(router-alive): 组件销毁有清理缓存和组件数据
Browse files Browse the repository at this point in the history
  • Loading branch information
bhuh12 committed Sep 4, 2019
1 parent 5835308 commit ab4bfd4
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 41 deletions.
4 changes: 4 additions & 0 deletions demo/components/frames/I18n.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export default {
}
},
destroyed () {
Vue.prototype.$lang = null
},
methods: {
i18n (key, params) {
let lang = this.lang[key]
Expand Down
60 changes: 40 additions & 20 deletions src/components/RouterAlive.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,42 @@ export default {
})
},

// 销毁后清理缓存
destroyed () {
this.removeAll()
this.cache = null
this.lastRoute = null
},

render () {
const slot = this.$slots.default
const vnode = getFirstComponentChild(slot)
const vmOpts = vnode && vnode.componentOptions

if (vmOpts) {
const { cache, $route, lastRoute } = this
const { $route, lastRoute } = this

// 如果是transition组件,页面组件则为子元素
const pageNode = vmOpts.tag === 'transition' ? vmOpts.children[0] : vnode

if (pageNode && pageNode.componentOptions) {
// 获取缓存
const key = this.getAliveId()
const cacheItem = cache[key]
const { vm: cacheVm, route: cacheRoute } = cacheItem || emptyObj
let cacheItem = this.get(key)
let { vm: cacheVm, route: cacheRoute } = cacheItem || emptyObj

// 是否需要重载路由强制刷新页面组件
let needReloadView = false

// 路由是否改变
let isRouteChange = lastRoute !== $route

// 是否跟上次路由共用组件
// 是否与上次路由相似
let likeLastRoute = this.isAlikeRoute($route, lastRoute)

// 是否跟上次路由不同单共用组件
let isShareComp = isRouteChange &&
!this.isAlikeRoute($route, lastRoute) &&
!likeLastRoute &&
this.getPageComp($route) === this.getPageComp(lastRoute)

if (isRouteChange) {
Expand All @@ -51,15 +61,24 @@ export default {
}

if (cacheVm) {
// 缓存组件的路由地址匹配则取缓存的组件
if (this.isAlikeRoute(cacheRoute, $route)) {
let ctorId = this.getCtorIdByNode(pageNode)
let lastCtorId = cacheVm._ctorId

// 页面实例组件构造函数改变则清理旧缓存,解决 webpack 热加载后组件缓存不更新
if (lastCtorId && lastCtorId !== ctorId) {
// 清理缓存组件
this.remove(key)
} else if (this.isAlikeRoute(cacheRoute, $route)) {
// 缓存组件的路由地址匹配则取缓存的组件
pageNode.componentInstance = cacheVm
} else {
// 缓存组件路由地址不匹配则销毁缓存并重载路由
cacheVm.$destroy()
cacheItem.vm = null
this.remove(key)
needReloadView = true
}

// 更新构造 id
cacheVm._ctorId = ctorId
}

// 共用组件的路由切换需重载路由
Expand Down Expand Up @@ -93,28 +112,29 @@ export default {
return (cache[key] = item)
},

// 获取缓存项
get (key) {
return this.cache[key]
},

// 删除缓存项
remove (key) {
const { cache } = this
const item = cache[key]
let item = this.get(key)

// 销毁组件实例
if (item) {
item.vm && item.vm.$destroy()
item.vm = null
delete cache[key]
}

this.$emit('remove', [ key ])
},

// 清理缓存
clear (key) {
const item = this.cache[key]
const vm = item && item.vm
if (vm) {
vm.$destroy()
item.vm = null
// 清理所有缓存
removeAll () {
const { cache } = this

for (let i in cache) {
this.remove(i)
}
}
}
Expand Down
13 changes: 10 additions & 3 deletions src/components/RouterTab/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ export default {
this.updateActivedTab()
},

destroyed () {
// 取消原型挂载
if (Vue.prototype.$routerTab === this) {
Vue.prototype.$routerTab = null
}
},

methods: {
// 根据初始页签数据生成页签列表
getTabItems () {
Expand Down Expand Up @@ -210,7 +217,7 @@ export default {
async refreshTab (id = this.activedTab) {
try {
await this.pageLeavePromise(id, 'refresh')
this.$refs.routerAlive.clear(id)
this.$refs.routerAlive.remove(id)
if (id === this.activedTab) this.reloadView()
} catch (e) {}
},
Expand All @@ -226,10 +233,10 @@ export default {
if (!force) {
try {
await this.pageLeavePromise(id, 'refresh')
$alive.clear(id)
$alive.remove(id)
} catch (e) {}
} else {
$alive.clear(id)
$alive.remove(id)
}
}
this.reloadView()
Expand Down
25 changes: 19 additions & 6 deletions src/components/RouterTab/matched.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ export default {
warn(pageRouteIdx > -1, '未能匹配到路由信息')

return {
baseRoute: matched[pageRouteIdx - 1],
pageRoute: matched[pageRouteIdx],
pageRouteIdx
baseRoute: matched[pageRouteIdx - 1], // 跟路由
pageRoute: matched[pageRouteIdx], // 页面路由
pageRouteIdx,
isNest: pageRouteIdx !== matched.length - 1 // 是否嵌套路由
}
},

Expand All @@ -31,6 +32,12 @@ export default {
return path
},

// 获取 vnode 构造 id
getCtorIdByNode (node) {
let { componentOptions: opts } = node
return opts ? opts.Ctor.cid : null
},

// 获取跟路径
getBasePath () {
let { path } = this.matchRoutes().baseRoute
Expand All @@ -41,17 +48,18 @@ export default {

// 获取嵌套路由的页面路径
getPagePath (route = this.$route, matchRoutes = this.matchRoutes(route)) {
let { pageRoute, pageRouteIdx } = matchRoutes
let { pageRoute, isNest } = matchRoutes

// 页面嵌套路由
if (pageRouteIdx !== route.matched.length - 1) {
if (isNest) {
return this.parsePath(pageRoute.path, route.params)
}
},

// 获取嵌套路由的页面组件
getPageComp (route = this.$route) {
return this.matchRoutes(route).pageRoute.components.default
let { pageRoute } = this.matchRoutes(route)
return pageRoute ? pageRoute.components.default : null
},

// 获取路由不带hash的路径
Expand All @@ -68,6 +76,11 @@ export default {

return this.getPathWithoutHash(route1) === this.getPathWithoutHash(route2) ||
(route1Path && route2Path && route1Path === route2Path)
},

// 是否嵌套路由
isNestRoute (route, matchRoutes = this.matchRoutes(route)) {
return matchRoutes.isNest
}
}
}
16 changes: 4 additions & 12 deletions src/mixins/routerPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,10 @@ export default {
})
},

// 解决webpack热加载后组件缓存不更新
activated () {
if (!this._isRouterPage) return false

let ctorId = this.$vnode.componentOptions.Ctor.cid

// 热加载后Ctor.cid改变
if (this._ctorId && this._ctorId !== ctorId) {
this.$destroy()
this.$routerTab.refreshTab()
// 销毁后清理数据
destroyed () {
if (this._isRouterPage) {
this.$vnode.data.routerAlive = null
}

this._ctorId = ctorId
}
}

0 comments on commit ab4bfd4

Please sign in to comment.