修改vue的keep-alive實現仿easyui-頁面tab切換
阿新 • • 發佈:2019-01-04
後臺管理頁面通常會有tabs切換作為導航
常見實現方式
- 通過顯示和隱藏div(缺點:無法看到路由)
- 通過iframe,其實和顯示隱藏區別不大
vue實現方式
因為要在vue中實現,用vue-router和vue中一個keep-alive,但是keep-alive有個缺點,他是用物件來快取元件,並且是一個抽象元件,所以就稍微修改下。
效果圖
功能
- 點選左側顯示toolbar nav
- 通過toolbar 切換路由,並保持之前快取
- 關閉toolbar清除快取,開啟後仍可用快取
專案原始碼
實現方式
監聽路由的變動,當路由改變時將當前路由新增到一個列表裡面。迴圈此列表生成toolbar
tabs
,給keep-alive新增兩個方法。第一個是當keep-alive工作的時候,設定儲存key
的hook、第二個方法新增通過key
刪除快取removeCacheByKey
。 當點選關閉按鈕的時候,呼叫
removeCacheByKey
就可以完美解決vue keep-alive無法主動清除快取的問題了。主要的keep-alive程式碼:
import _ from 'lodash'
function isDef (val) {
return val !== undefined && val !== null
}
function getFirstComponentChild (children) {
if (Array.isArray(children)) {
for (let i = 0; i < children.length; i++) {
const c = children[i]
if (isDef(c) && isDef(c.componentOptions)) {
return c
}
}
}
}
function getComponentName (opts) {
return opts && (opts.Ctor.options.name || opts.tag)
}
function matches (pattern, name) {
if (Array.isArray(pattern)) {
return pattern.indexOf(name) > -1
} else if (typeof pattern === 'string') {
return pattern.split(',').indexOf(name) > -1
} else if (_.isRegExp(pattern)) {
return pattern.test(name)
}
/* istanbul ignore next */
return false
}
function pruneCache (cache, current, filter) {
for (const key in cache) {
const cachedNode = cache[key]
if (cachedNode) {
const name = getComponentName(cachedNode.componentOptions)
if (name && !filter(name)) {
if (cachedNode !== current) {
pruneCacheEntry(cachedNode)
}
cache[key] = null
}
}
}
}
function pruneCacheEntry (vnode) {
if (vnode) {
vnode.componentInstance.$destroy()
}
}
export default {
name: 'pk-keep-alive',
props: {
include: [],
exclude: [],
updateComponentsKey: Function
},
created () {
// vue的keep-alive儲存物件
this.cache = Object.create(null)
},
// 呼叫keep-alive元件銷燬鉤子,元件銷燬的時候同時清除快取
destroyed () {
for (const key in this.cache) {
pruneCacheEntry(this.cache[key])
}
},
watch: {
include (val) {
pruneCache(this.cache, this._vnode, name => matches(val, name))
},
exclude (val) {
pruneCache(this.cache, this._vnode, name => !matches(val, name))
}
},
render () {
const vnode = getFirstComponentChild(this.$slots.default)
const componentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
const name = getComponentName(componentOptions)
if (name && (
(this.include && !matches(this.include, name)) ||
(this.exclude && matches(this.exclude, name))
)) {
return vnode
}
const key = vnode.key == null
// same constructor may get registered as different local components
// so cid alone is not enough (#3269)
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
// 新增獲取key的外部hook
this.updateComponentsKey && this.updateComponentsKey(key)
if (this.cache[key]) {
vnode.componentInstance = this.cache[key].componentInstance
} else {
this.cache[key] = vnode
}
vnode.data.keepAlive = true
}
return vnode
},
methods: {
// 通過cache的key刪除對應的快取
removeCacheByKey (key) {
pruneCacheEntry(this.cache[key])
this.cache[key] = null
}
}
}