vue 应用缓存 iframe 页面

1,423 阅读2分钟

vue 应用缓存 iframe 页面

在我们平常开发项目中,可能会存在一些其它项目嵌入的问题,如果项目嵌入过多,可以考虑使用微前端的方式。使用微前端在项目嵌入不多的情况,不建议用,一是初期需要花费不少时间,二是需要上手学习,都需要不少的成本时间。其实早在微前端之前,应用嵌入大多数都是使用的 iframe 嵌入方式。这个使用起来比较方便,但也有一些问题,比如应用之前的数据传递问题,还有就是应用内的弹框问题。在数据传递这一方面可以使用 postMessage 来实现数据的传递。使用 iframe 嵌入应用,页面缓存显得很关键,在没有缓存的情况下,每次切换到对应的 iframe 菜单,重新加载应用,这样是很浪费时间,影响用户体验。

实现思路

1、在 iframe 页面路由注册时,通过一定的规则区分 iframe 路由页面和非 iframe 路由页面。

2、手动获取所有的 iframe 路由页面数据,并且手动注册所有 iframe 路由页面。

3、页面初始化和路由切换时设置对应的 iframe 路由页面应用为已打开状态。

4、获取所有的已打开 iframe 路由页面数据,使用 v-for 循环,并且使用 v-show 控制页面显示隐藏。v-show 就是实现缓存的关键。

区分路由页面

通过在注册路由时在 meta 数据里面添加一个标识,用以区分是否 iframe 路由页面。 代码:通过 isIframe 标识控制是否是 iframe 页面

  export default {
  path: '/parent',
  component: Layout,
  redirect: '/parent/index',
  name: 'Parent',
  meta: {
    title: '父菜单',
    icon: 'example'
  },
  children: [{
    path: 'child',
    name: 'Child',
    iframeComponent: () => import('@/views/parent/child'),
    meta: {
      title: '子菜单',
      isIframe: true,
      icon: 'example'
    }
  },
  ]

获取所有 iframe 路由页面并且手动注册

// 获取所有iframe路由页面
methods:{
  getComponentsArr() {
    const {routes = [] } =  this.$router.options
    const data = routes.reduce((pre, item) => {
      if (!Array.isArray(item.children)) return pre
      item.children.forEach((cItem) => {
        const { meta = {}, name, path, iframeComponent } = cItem
        if (meta.isIframe) {
          const obj = {
            name,
            path: item.path + '/' + path,
            hasOpen: false, // 是否打开过,默认false
            component: iframeComponent
          }
          pre.push(obj)
        }
      })
      return pre
    }, [])
    return data
  },
},
created:{
  const componentsArr = this.getComponentsArr()
  // 遍历注册iframe路由页面
  componentsArr.forEach((item) => {
    Vue.component(item.name, item.component)
  })
}

设置 iframe 路由在打开后标记为打开状态,方便缓存

methods:{
// 通过设置iframe路由页面的hasOpen属性获取页面是否打开过
  isOpenIframePage() {
    const target = this.componentsArr.find(item => {
      return item.path === this.$route.path
    })
    if (target && !target.hasOpen) {
      target.hasOpen = true
    }
  },
},
computed:{
  // 实现懒加载,只渲染已经打开过(hasOpen:true)的iframe页
  hasOpenComponentsArr() {
    return this.componentsArr.filter(item => {
      return item.hasOpen
    })
  }
}

使用 iframe 路由组件

// v-show 控制组件的显示隐藏 实现类似于缓存的机智
  <component v-for="item in hasOpenComponentsArr"
      :key="item.name"
      v-show="item.path===$route.path"
      :is="item.name"></component>

完整代码

<template>
  <div class="app-mainWrap">
    <section class="app-main">
      <template >
        <keep-alive v-if="cachedViews">
          <router-view/>
        </keep-alive>
        <router-view v-else></router-view>
      </template>
      <!--iframe页(keep-alive 不能缓存iframe 单独通过v-show 控制)-->
      <component v-for="item in hasOpenComponentsArr"
                 :key="item.name"
                 v-show="item.path===$route.path"
                 :is="item.name"></component>
    </section>
  </div>
</template>

<script>
export default {
  name: 'AppMain',
  data() {
    return {
      componentsArr: [],
    }
  },
  computed: {
    // 实现懒加载,只渲染已经打开过(hasOpen:true)的iframe页
    hasOpenComponentsArr() {
      return this.componentsArr.filter(item => {
        return item.hasOpen
      })
    }
  },
  watch: {
    // 判断当前路由是否iframe页
    $route: {
      handler(to, from) {
        this.isOpenIframePage()
      }
    }
  },
  created() {
    // 设置iframe页的数组对象
    const componentsArr = this.getComponentsArr()
    componentsArr.forEach((item) => {
      Vue.component(item.name, item.component)
    })
    this.componentsArr = componentsArr
    // 判断当前路由是否iframe页
    this.isOpenIframePage()
  },
  methods: {
    // 根据当前路由设置hasOpen
    isOpenIframePage() {
      const target = this.componentsArr.find(item => {
        return item.path === this.$route.path
      })
      // console.log(target, 12313)
      if (target && !target.hasOpen) {
        target.hasOpen = true
      }
    },
    // 遍历路由的所有页面,把含有iframeComponent标识的收集起来
    getComponentsArr() {
      const router = this.$router
      const routes = router.options.routes
      const data = routes.reduce((pre, item) => {
        if (!Array.isArray(item.children)) return pre
        item.children.forEach((cItem) => {
          const { meta = {}, name, path, iframeComponent } = cItem
          if (meta.isIframe) {
            const obj = {
              name,
              path: item.path + '/' + path,
              hasOpen: false, // 是否打开过,默认false
              component: iframeComponent
            }
            pre.push(obj)
          }
        })
        return pre
      }, [])
      return data
    },
  }
}
</script>

结语

主要是工作中问题的一些个人记录,文章比较简单,主要是为了加深个人记忆及供掘友们参考。

自己写的难免有一些不足之处,欢迎掘友指正。

参考文章:vue缓存iframe的解决方法