vue3 ,用component:is实现tab选项卡内容动态切换,怎么配置缓存组件的主动移除?

在菜单组件选中事件代码中,通过 defineAsyncComponent 动态导入组件,并且不注册的情况下,赋值给 component 的 :is 绑定的属性,实现局部无组件注册的动态组件渲染。

要做 tab 切换动态管理,每个选项卡对应的内容都要缓存,哪怕是同一个控件,而且还是带关闭按钮的那一种 tab 切换管理。点击关闭按钮移除选项卡对应动态组件的缓存(keep-alive)。在关闭选项卡后,如何移除该选项卡显示的组件的缓存?

如果不是依赖全局动态注册组件,也就是基于 app.component(key,defineAsyncComponent(() => import('@/views/xxx.vue'))) 这种形式,能做到吗?怎么做?

按照下面的代码,应该还要加点什么配置?

相关的vue组件代码

<template>
    <div class="box">
      <div class="content5">
        <div class="aside" >
          <el-menu
        class="el-menu-vertical-demo">
        <template v-for="(item) in menuItems">
            <el-menu-item  :index="item.key" @click="menuClick(item)" >{{item.label}}</el-menu-item>
        </template>
          </el-menu>
        </div>
          <div class="tabs">
          <div class="tabHeader">
           <div>
            <el-tabs v-model="current_content.activeName" @tab-click="tabHeaderClick" @tab-remove="removeTab" >
              <el-tab-pane  v-for="(item) in tab_list" :label="item.label" :name="item.code" :key="item.code" closable  >
              </el-tab-pane>
            </el-tabs>
           </div>
          </div>
          <div class="tabContent">
            <keep-alive   >
                <component :is="current_content.view"   ></component>
              </keep-alive>
          </div>
        </div>
      </div>
    </div>
  </template>
<script setup>

import {ref,reactive,defineAsyncComponent } from "vue"  

//左侧菜单
let menuItems=reactive([]);
//tab导航
let tab_list=reactive([])

let current_content=reactive({
    seq:1,
    view_name:undefined,
    view:undefined,
    activeName:undefined,
});

// let currentContent=ref(undefined);

const menuClick=(menu)=>{
  addTab(menu.label,menu.key,menu.view);
  switchChildPage2(menu.key,menu.view);
}




const switchChildPage2=(key,view)=>{
  if(key==current_content.view_name){
    return;
  }
  current_content.view_name=key;
  current_content.view=view;
  current_content.activeName=key;

};

const addTab=(title,key,view)=>{
      var item = tab_list.find((item) => item.code === key);
      if(item!=null){
        return;
      }
      var addTabItem={label:title,code:key,view:view}
      tab_list.push(addTabItem);
}

const removeTab=(val)=>{
      var index = tab_list.findIndex((item) => item.code === val);
      if(index==-1){
        return;
      }
        current_content.view_name=undefined;
        current_content.view=undefined;
        current_content.activeName=undefined;

      tab_list.splice(index, 1);

      if(tab_list.length!=0){
        var fisrt=tab_list[0];
        current_content.view_name=fisrt.key;
        current_content.view=fisrt.view;
        current_content.activeName=fisrt.key;
      }
    };

    

    const tabHeaderClick=(val)=>{
  
      var item = tab_list.find((item) => item.code === val.props.name);

      switchChildPage2(item.code,item.view);
    };

const load=()=>{
    //测试动态获取菜单
    var test1=[
         {
            title:"测试1",
            component:  defineAsyncComponent(()=>import('@/views/admin/test/test1.vue')),
          },
          {
              title:"测试2",
              component:  defineAsyncComponent(()=>import('@/views/admin/test/test2.vue')),
             
            },
            
      ];
    //添加左侧菜单
    for(var item2 of test1){
        var menu={
          label:item2.title,
          key:'a'+current_content.seq,
          view:item2.component,
        };
        current_content.seq++;
        menuItems.push(menu);
    }
}

load();
</script>
阅读 4.3k
1 个回答

使用 keep-alive 的include,切换到tab时,将组件name添加到include,关闭tab时,将组件name移除

app.vue

<template>
  <div>
    <el-menu class="el-menu-vertical-demo" :default-active="curTab">
      <template v-for="(item, index) in menus">
        <el-menu-item  :index="item.title" @click="onMenuClick(item)">
          {{item.title}}
        </el-menu-item>
      </template>
    </el-menu>
    <el-tabs v-model="curTab" @tab-click="onTabClick" @tab-remove="removeTab">
      <el-tab-pane  v-for="item in tabs" :label="item.title" :name="item.title" :key="item.title" closable>
      </el-tab-pane>
    </el-tabs>
    <keep-alive :include="caches">
      <component :is="curTabComp"></component>
    </keep-alive>
    {{ caches }}
  </div>
</template>

<script setup>
import {ref, defineAsyncComponent, shallowRef, markRaw, nextTick, h, defineComponent } from "vue" 

function lazy (name, source) {
  return defineComponent({
    name, 
    render: () => h(defineAsyncComponent(source))
  })
}

const menus = ref([
 {
    title:"测试1",
    component: markRaw(lazy('test1', ()=>import('./test/test1.vue'))),
    name: 'test1'
  },
  {
    title: "测试2",
    component: markRaw(lazy('test2', ()=>import('./test/test2.vue'))),
    name: 'test2'
  },
  {
    title: "测试3",
    component: markRaw(lazy('test3', ()=>import('./test/test3.vue'))),
    name: 'test3'
  }
])
const tabs = ref([])
const curTab = ref('')
const curTabComp = shallowRef()
const caches = ref([])

function onMenuClick (tab) {
  if (!tabs.value.includes(tab)) tabs.value.push(tab)
  if (!caches.value.includes(tab.name)) caches.value.push(tab.name)
  
  nextTick(() => {
    curTabComp.value = tab.component
    curTab.value = tab.title
  })
}

function onTabClick ({paneName}) {
  const tab = Array.from(tabs.value).find(item => item.title === paneName)
  if (tab) {
    curTabComp.value = tab.component
    curTab.value = tab.title
  }
}

function removeTab (paneName) {
  const tab = tabs.value.find(item => item.title === paneName)
  if (tab) {
    caches.value = caches.value.filter(item => item !== tab.name)
  }
  
  tabs.value = tabs.value.filter(item => item.title !== paneName)
  
  const first = tabs.value[0]
  curTabComp.value = first?.component
  curTab.value = first?.title
}
</script>

<style lang='less' scoped>
</style>

下面是:test1.vue

<template>
  <div>
    组件1 <br> {{ new Date().toLocaleTimeString() }}
  </div>
</template>

<script setup lang="ts">
import { onMounted } from 'vue'

onMounted(() => {
  console.log('test1 mounted')
})
</script>

<style lang='less' scoped>
</style>

test2.vue

<template>
  <div>组件2 <br> {{ new Date().toLocaleTimeString() }}</div>
</template>

<script setup lang="ts">
import { onMounted } from 'vue'

onMounted(() => {
  console.log('test2 mounted')
})
</script>

<style lang='less' scoped>
</style>
推荐问题