修改vue的keep-alive实现仿easyui-页面tab切换

来源:互联网 发布:微信小游戏源码下载 编辑:程序博客网 时间:2024/05/16 13:06

后台管理页面通常会有tabs切换作为导航

常见实现方式

  1. 通过显示和隐藏div(缺点:无法看到路由)
  2. 通过iframe,其实和显示隐藏区别不大

vue实现方式

因为要在vue中实现,用vue-router和vue中一个keep-alive,但是keep-alive有个缺点,他是用对象来缓存组件,并且是一个抽象组件,所以就稍微修改下。

效果图

navigation

功能

  1. 点击左侧显示toolbar nav
  2. 通过toolbar 切换路由,并保持之前缓存
  3. 关闭toolbar清除缓存,打开后仍可用缓存

项目源代码

https://github.com/slipkinem/vue-admin

实现方式

监听路由的变动,当路由改变时将当前路由添加到一个列表里面。循环此列表生成toolbartabs,给keep-alive添加两个方法。第一个是当keep-alive工作的时候,设置存储keyhook、第二个方法添加通过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    }  }}