vue动态路由iframe外链懒加载

2,829 阅读1分钟

需求:动态路由要嵌入外链页面,需要传token,需要懒加载,需要url能处理一下不要太暴露

思路借鉴:juejin.cn/post/684490…

写的比较粗糙,肯定有优化空间,抛砖引玉,抛砖引玉

1、在生成动态路由过程中顺便生成iframe的列表:

// 当遍历到iframe节点时
if(oMenu.path.includes('http')){
  let hasFlag = false    // 是否已含有该项
  // 从localStorage中取iframeList,取不到则取空数组
  let iframeList = localStorage.getItem('iframeList') ? JSON.parse(localStorage.getItem('iframeList')) : []
  // 处理相关信息
  let obj = {
    name: oMenu.name,
    label: oMenu.path.split('/').length===7?'page-' + oMenu.path.split('/')[6]:oMenu.path,
    id: oMenu.id,
    path: oMenu.path
  }
  for(let i=0;i<iframeList.length;i++){
    if(iframeList[i].id === obj.id){
      // 检测id相同时覆盖信息
      iframeList[i] = obj
      hasFlag = true
    }
  }
  // 不存在相同id项则push
  !hasFlag && iframeList.push(obj)
  localStorage.setItem('iframeList',JSON.stringify(iframeList))
}

生成的数据大概这样子:

[
    {label: "page-cost-supplement-list", name:"xxx",path:"http://xxx/xx/xx/xx/cost-supplement-list"},
    ...
]

再顺便添加一个iframe专用的getPath方法,后面要用

getIframePath(params){
  const { page } = params
  let result = page || '/'
  if (page.includes('page-')) {
    result = `/out/page?${objToform(params)}`
  }
  return result
},

2、在固定router里头添加iframe相关路由

{
    path: '/out', // 可以改,与后面代码对应
    component: Layout,
    redirect: '/out',
    children: [{
        path: ":page",  // 可以改,与后面代码对应
        name: 'iframe',
        component: () =>
            import ( /* webpackChunkName: "page" */ '@/components/iframe/main'),
        props: true
    }]
},

3、处理左边菜单栏点击跳转方法

open(item) {
  if (this.screen <= 1) this.$store.commit("SET_COLLAPSE");
  this.$router.$avueRouter.group = item.group;
  // 处理iframe情况
  if(/^http/.test(item.path)){
    this.$router.push({
      path: this.$router.$avueRouter.getIframePath({
        // 本来是用src的,但是src太明显了,遮掩一下,后续也因此要做对应处理
        page: this.changePath(item)
      }),
      query: item.query,
    }).catch(() => {});
  }else{
    this.$router.push({
      path: this.$router.$avueRouter.getPath({
        name: item[this.labelKey],
        src: item[this.pathKey]
      }),
      query: item.query
    }).catch(() => {});
  }
}

4、处理路由导航守卫中页面跳转时修改taglist的方法

let value = to.query.src || to.fullPath
const label = to.query.name || to.name
if (meta.isTab !== false && !validatenull(value) && !validatenull(label)) {
  // iframe情况,因为上面src变成page了要做对应处理
  if( to.query.page && to.query.page.includes('page-') ){
    value = to.query.page
    const arr = JSON.parse(localStorage.getItem('iframeList'))
    store.commit('ADD_TAG', {
      //  做对应转换使多标签tab页能正常显示对应名称
      label: arr.filter(item => item.label === value)[0].name,
      value: value,
      params: to.params,
      query: to.query,
      group: router.$avueRouter.group || [],
    })
  }else{
    store.commit('ADD_TAG', {
      label: label,
      value: value,
      params: to.params,
      query: to.query,
      group: router.$avueRouter.group || [],
    })
  }
}
next()

5、接下来要处理iframe组件(这里就是借鉴的思路糅合进来了)

<template>
  <div>
    <basic-container>
      <keep-alive>
        <router-view></router-view>
      </keep-alive>
      <component
        v-for="item in hasOpenComponentsArr"
        :key="item.name"
        :is="item.name"
        v-show="item.label === getLabel($route.query.page)"
      ></component>
    </basic-container>
  </div>
</template>
export default {
  data () {
    return {
      componentsArr: [],
      clientHeight: '',
    }
  },
  created () {
    let componentsArr = this.getComponentsArr()
    componentsArr.forEach(item => {
      // 这里是重点,卡了我好久
      // Vue.component(item.name,{template:`<iframe src='${this.getSrc(this.$route.query.page)}' class="iframe"></iframe>`})
      // 才发现上面写的有问题!!!!应该下面这样写
      let str = item.path +'?token='+ token
      Vue.component(item.name,{template:`<iframe src='${str}' class="iframe"></iframe>`})
    })
    this.componentsArr = componentsArr
    this.isOpenIframePage();
  },
  props: ['routerPath'],
  watch: {
    $route: function () {
      this.isOpenIframePage();
    },
  },
  computed: {
    // 实现懒加载,只渲染已经打开过(hasOpen:true)的iframe页
    hasOpenComponentsArr() {
      return this.componentsArr.filter(item => item.hasOpen);
    }
  },
  methods: {
    getLabel(val){
      const arr = JSON.parse(localStorage.getItem('iframeList'))
      return arr.filter(item => item.label === val)[0].name
    },
    getSrc(val){
      // 要附带的token就在这里添加进去
      let token = JSON.parse(localStorage.getItem("pig-access_token")).content
      const arr = JSON.parse(localStorage.getItem('iframeList'))
      return arr.filter(item => item.label === val)[0].path + '?token=' + token
    },
    // 根据当前路由设置hasOpen
    isOpenIframePage() {
      const target = this.componentsArr.find(item => {
        return item.label === this.getLabel(this.$route.query.page)
      });
      if (target && !target.hasOpen) {
        target.hasOpen = true;
      }
    },
    // 遍历路由的所有页面,把含有iframeComponent标识的收集起来
    getComponentsArr() {
      const iframeArr = JSON.parse(localStorage.getItem('iframeList'))
      console.log(iframeArr)
      return iframeArr.map((item) => {
        return {
          label: item.name,
          // 这个name就是上面注册组件的名字,由于我这里附带信息没有好用的所以这样做
          // 注意组件注册的名字规定
          name: 'a'+item.id, 
          path: item.path,
          hasOpen: false, // 是否打开过,默认false
        }
      })
    },
  }
}
</script>

由于使用了Vue.component注册组件不要忘了在vue.config.js里做修改

module.exports = {
  runtimeCompiler: true, // 这一行就够了
}

6、多页签tab的点击方法也要做对应修改

openTag(item) {
  let tag;
  if (item.name) {
    tag = this.findTag(item.name).tag;
    // 如果标签地址和目前地址不同才跳转,否则会报错
    if(item.name != this.$route.path){
      // 处理iframe
      if(item.name.includes('page-')){
        // 其实也没啥,主要就是把src换成page
        this.$router.push({
          path: this.$router.$avueRouter.getIframePath({
            page: tag.value
          }),
        })
      }else{
        this.$router.push({
          path: this.$router.$avueRouter.getPath({
            name: tag.label,
            src: tag.value
          }),
          query: tag.query
        })
      }
    }
  }
},

这样就完成了 大概效果像这样

效果1.jpg

效果2.jpg 第一次分享经验,比较粗糙,还望多包涵