SoFunction
Updated on 2025-04-04

Method to implement keep alive for iframe in Vue

Preface

A recent requirement requires that the page containing an iframe is added to the Vue project, and the content of the iframe is required not to be refreshed during the routing switch. At the beginning, I found that it was useless by using the keep-alive that comes with Vue, so I studied the solution myself. . . . . .

Vue's keep-alive principle

To achieve the state of the iframe page. Let's first figure out why Vue's keep-alive cannot work. The keep-alive principle is to keep the node information in the component in VNode (in memory), and render it from Vnode to the real DOM when rendering is needed. The content in the iframe page does not belong to the node's information, so using keep-alive will still re-render the content in the iframe. In addition, I have also tried to have an idea: if the entire iframe node is saved and then rendered to the target node when it needs to be switched, can the iframe page be not refreshed? ————It is also not feasible. Every time an iframe is rendered, it is equivalent to opening a new web page window. Even if the node is saved, the iframe page will still be refreshed during rendering.

Ideas for realization

Since it is difficult to maintain the state in the iframe page, I thought of another method at this time. Can I make some moves on the route-view node of Vue? This allows Vue routes to be used when switching non-iframe pages, and v-show to switch display and hide, so that the iframe nodes are not deleted, so that the iframe state can be maintained.

Let's simply implement the above effects, above code:

Entrance:

import Vue from 'vue/dist/'
import App from './'
import VueRouter from 'vue-router';

const Index = { template: '<div>Index</div>' }
const routes = [
 // Two pages containing iframes {
 path: '/f1',
 name: 'f1'
 },
 // Two pages containing iframes {
 path: '/f2',
 name: 'f2'
 },
 {
 path: '/index',
 component: Index
 }
]

const router = new VueRouter({
 routes
});

(VueRouter);
new Vue({
 render: h => h(App),
 router
}).$mount('#app')

Root Component:

<template>
 <div >
 <div class="nav">
  <router-link class="router" to="/f1">Go to F1</router-link>
  <router-link class="router" to="/f2">Go to F2</router-link>
  <router-link class="router" to="/index">Go to Index</router-link>
 </div>
 
 <keep-alive>
  <!-- VueRouting -->
  <router-view></router-view>
 </keep-alive>
 
 <!-- iframepage -->
 <f1 v-show="$ == '/f1'"></f1>
 <f2 v-show="$ == '/f2'"></f2>
 </div>
</template>

<script>
import F1 from './components/f1';
import F2 from './components/f2';
export default {
 name: 'app',
 components: {
 F1,
 F2
 },
 
}
</script>

To put it simply, the key point is that when initializing the route, the iframe page does not fill in the attribute component, so that the page will be blank. Then render the iframe page component next to the router-view node, use $ to determine the pointing of the current route, and control the display and hiding of the iframe page.

The above code simply solves the problem, but there are still some places that can be optimized:

  1. The iframe page has been rendered when the root node is rendered. This iframe page can be made lazy to load. Only when the corresponding page has been entered, the rendering is triggered, and after rendering, use v-show to switch display and hide using v-show to switch display and hide after rendering.
  2. Whenever an iframe page is added, a piece of component is added to introduce registration and calling code. More complicated. Our goal should be to add as little code as possible for each iframe page. Here's the idea:
    1. Define an attribute in the routing configuration to identify whether the page contains an iframe
    2. According to the logo, the iframe page component automatically registers and renders dynamically, without the need to hand-written additional code
    3. The logic of router-view and iframe switching is encapsulated into a new component, and it is used to replace the original router-view

We first modify the router configuration and add a property name iframeComponent to identify whether the iframe is included. The value of this property is a component file reference

:

import F1 from './components/f1';
import F2 from './components/f2';

const routes = [
 {
 path: '/f1',
 name: 'f1',
 iframeComponent: F1 // Used to identify whether an iframe page is included },
 {
 path: '/f2',
 name: 'f2',
 iframeComponent: F2 // Used to identify whether an iframe page is included },
 {
 path: '/index',
 component: { template: '<div>Index</div>' }
 }
]

const router = new VueRouter({
 routes // (Abbreviation) is equivalent to routes: routes});

new Vue({
 render: h => h(App),
 router
}).$mount('#app')

Next, we combine the second and third steps to encapsulate the new components:

<template>
 <div>
  <!-- Vueofrouter-view -->
  <keep-alive>
   <router-view></router-view>
  </keep-alive>

  <!-- iframePage -->
  <component
   v-for="item in hasOpenComponentsArr"
   :key=""
   :is=""
   v-show="$ === "
  ></component>
 </div>
</template>

<script>
import Vue from 'vue/dist/'
export default {
 created() {
  // Set the array object of the iframe page  const componentsArr = ();
  ((item) => {
   (, );
  });
   = componentsArr;
  // Determine whether the current route is iframe page  ();
 },
 data() {
  return {
   componentsArr: [] // Page with iframe  }
 },
 watch: {
  $route() {
   // Determine whether the current route is iframe page   ();
  }
 },
 computed: {
  // Implement lazy loading and only render iframe pages that have been opened (hasOpen:true)  hasOpenComponentsArr() {
   return (item => );
  }
 },
 methods: {
  // Set hasOpen according to the current route  isOpenIframePage() {
   const target = (item => {
    return  === this.$
   });
   if (target && !) {
     = true;
   }
  },
  // traverse all pages of the route and collect the iframeComponent ID  getComponentsArr() {
   const router = this.$router;
   const routes = ;
   const iframeArr = (item => );
   
   return ((item) => {
    const name =  || ('/', '');
    return {
     name: name,
     path: ,
     hasOpen: false, // Whether it has been turned on, default false     component:  // Reference to component files    };
   });
  }
 }
}
</script>

  1. What this component mainly does is to generate an array object containing only an iframe page based on the routes in it.
  2. Listen to $route on watch. If you determine that the current page is in the iframe page list, set the hasOpen attribute to true and render the component
  3. Use v-show="$ ===" to switch the display and hiding of the iframe page.

The logic is not complicated, so I won't go into details here.

Conclusion

If you have a better implementation method, or if I have any errors that need to be corrected above, please feel free to communicate. The above demo code is placed on personal github/jmx164491960/vue-iframe-demo

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.