首页 > 其他分享 >【vue与iframe通讯】

【vue与iframe通讯】

时间:2024-05-25 22:59:02浏览次数:20  
标签:vue const eventsPub 通讯 iframe data 页面

vue 与 iframe 通讯

前言:vue嵌套iframe实现步骤

发送数据

vue 向 iframe 发送数据

	// utils/common.js
 
	/** vue向iframe发送数据
	  * content iframe.contentWindow
	  * type 事件类型
	  * data 传送的数据
	  */
	export function sendPostMessage(content, type, data = {}) {
		content.postMessage({ type, data }, '*');
	}

iframe 向 vue 发送数据

 // update.html & base.html & includes.html ...
 
 function sendPostMessage(type, data = {}) {
     window.parent.postMessage({ type, data }, "*");
 }

接收信息( vue & iframe 通用)

  // utils/iframe-message.js
  
  /**
   * 接收页面 postMessage 发送的信息
   * Pubsub提供多种类型供订阅,使用方法如下:
   * 1. 需要接收webSocket的地方,import eventsPub from '本文件路径'
   * 2. eventsPub.on(类型, callback)
   *    例:
   *      const receive = data => console.log('eventName', data)
   *      eventsPub.on('eventName', receive)
   * 3.不需要继续接收时调用
   *    eventsPub.remove("eventName", receive) 移除callback
   */

  /** 重要: postMessageEvent 中的 type 需提前定义 */
  class PubSub {
    list = {};
    constructor(events) {
      this.list = {};
      events.forEach(v => {
        this.list[v] = [];
      });
    }

    on(ev, callback) {
      if (this.list[ev]) {
        this.list[ev].push(callback);
        return true;
      } else {
        return false;
      }
    }

    emit(ev, data) {
      // data拷贝: 防止其他callback修改data
      const dataStr = JSON.stringify(data);
      if (this.list[ev]) {
        this.list[ev].forEach((v) => {
          try {
            v(JSON.parse(dataStr));
          } catch (err) {
            console.log("callback error:", err, v);
          }
        });
        return true;
      } else {
        return false;
      }
    }

    remove(ev, callback) {
      if (callback && this.list[ev]) {
        this.list[ev].forEach((v, i) => {
          if (v === callback) {
            this.list[ev].splice(i, 1);
          }
        });
        return true;
      } else {
        return false;
      }
    }
  }
  // 订阅的类型需要在postMessageEvent中提前定义好
  const postMessageEvent = ["PAGE_ISREADY"]
  const eventsPub = new PubSub(postMessageEvent);
  window.addEventListener("message", function (e) {
    if (e.data?.type) {
      eventsPub.emit(e.data.type, e.data.data)
    }
  });

  export default eventsPub;

实现相互通讯

通讯流程图

vue路由更新 及 iframe地址刷新时,两者之间的通讯流程
流程图

实现代码

vue 页面

  // IframeTemplate.vue
  
  import { onMounted, onBeforeUnmount } from "vue";
  import eventsPub from "@utils/iframe-message.js"
  import { sendPostMessage } from "@utils/common.js"

  onMounted(() => {
    // 接收信息
    eventsPub.on("PAGE_ISREADY", pageIsReady)
  });

  onBeforeUnmount(() => {
    eventsPub.remove("PAGE_ISREADY", pageIsReady)
  });

  function pageIsReady() {
      // console.log("PAGE_ISREADY")
      updateIframeLoginInfo()
  }

  // 发送信息
  /** iframe 页面登录信息同步 */
  function updateIframeLoginInfo() {
    const iframeWindow = $("#common-iframe")[0].contentWindow
    sendPostMessage(iframeWindow, "LOGIN_INFO", {
      token: localStorage.getItem("TOKEN"),
      userInfo: localStorage.getItem("USER") || "{}"
    })
  }

iframe页面

	// utils/iframe-message.js
	  
	// 将该文件写成原生写法: 删除 remove 之后的代码
	// 添加下边的代码
	
	/** 创建消息接收实例 */
	const postMessageEvent = ["LOGIN_INFO"]
	const eventsPub = new PubSub(postMessageEvent);
	
	/** 接收父页面的消息 */
	window.addEventListener("message", function (e) {
		if (e.data?.type) {
			eventsPub.emit(e.data.type, e.data.data)
		}
	});
	
	/** 发送消息 */
	function sendPostMessage(type, data = {}) {
		window.parent.postMessage({ type, data }, "*");
	}
  // base.html、includes.html、update.html...
  
  // 在所有的页面中引入iframe-message.js、jquery.js
  <script type="text/javascript" src="./js/jquery.js"></script>
  <script type="text/javascript" src="./js/iframe-message.js"></script>
  // 并添加下边的代码
  <script type="text/javascript">

    /** 获取当前登录用户信息并存储 */
    function setUserInfo(data) {
      const { token, userInfo } = data;
      const curToken = localStorage.getItem("TOKEN")
      const curUser = localStorage.getItem("USER")
      curToken != token && localStorage.setItem("TOKEN", token)
      curUser != userInfo && localStorage.setItem("USER", userInfo)
    }

    $(document).ready(() => {
      // GET VUE MESSAGE
      eventsPub.on("LOGIN_INFO", setUserInfo)

      // NOTIFY VUE MESSAGE
      sendPostMessage("PAGE_ISREADY", { iframeIsReady: true })
    })
  </script>

iframe内部重定向访问地址,更新vue路由

需将iframe的地址添加到vue路由上

  1. iframe页面判断是否是内部跳转,并将页面地址发送到vue

        // base.html...
        
        function setUserInfo(data) {
            // 其他...
            localStorage.setItem("FROM_PARENT", true)
        }
        $(document).ready(() => {
            // 其他...
            /** iframe内部页面跳转,加载完成 -  */
            const fromParent = localStorage.getItem("FROM_PARENT")
            localStorage.removeItem("FROM_PARENT")
            // 判断是否是vue 页面重定向的
            const { pathname, href, search } = window.location
            let path = pathname.split(".")[0];
            sendPostMessage("PAGE_ISREADY", !fromParent ? {} : { path })
        })
        ```
    
    
  2. vue 接收到地址后,页面路由修改,但iframe页面不需要再刷新

        // IframeTemplate.vue
        
        // 监听路由变更
        watch(route, () => {
          const historyParams = history.state.params
          // 只更新路由时,iframe 页面地址不更新
          if (historyParams && historyParams.justRoute && isFrameSrcUpdate == route.path && !isMounted) {
            isFrameSrcUpdate = "";
            return;
          }
          // iframe 页面地址更新
          createIframe()
        });
        
        /** iframe 页面加载完毕
         * 1. 登陆信息同步
         * 2. iframe 内部跳转,页面地址变化后,vue route也修改(但页面不刷新)
         */
        function pageIsReady(data) {
          updateIframeLoginInfo()
          if (data?.path) {
            let { path, name, query } = data
            isFrameSrcUpdate = path;
            const exit = router.getRoutes().find(i => i.path == path)
            /** iframe 发送了一个未添加路由的页面
             *  1. 添加该页面路由,为能正常访问
             *  2. 在当前页面刷新后,会有路由不存在的问题,
             *    需在整体添加路由的位置将当前页面添加进进去
             */
            !exit && router.addRoute({
              path,
              name,
              meta: { isIframe: true },
              component: () => import("../views/IframePage.vue")
            })
            // justRoute:只更新路由,不刷新页面
            router.push({ path, query, state: { params: { justRoute: true } } });
          }
        }
    

代码下载

查看代码地址

标签:vue,const,eventsPub,通讯,iframe,data,页面
From: https://blog.csdn.net/qq_32768761/article/details/139097646

相关文章

  • C#开发的通讯调试工具
    一款基于C#开发的通讯调试工具(支持ModbusRTU、MQTT调试) 前言今天大姚给大家分享一款基于C#、WPF、Prism、MaterialDesign、HandyControl开发的通讯调试工具(支持ModbusRTU、MQTT调试,界面色彩丰富):Wu.CommTool。工具特点工具界面色彩丰富。支持ModbusRTU、MQTT服务器、M......
  • Django+Vue构建前后端分离开发模式
    将Django作为后端,Vue作为前端进行前后端分离开发是一个常见的模式。下面是一个完整的步骤,以构建一个Django和Vue整合的项目。1.准备Django后端安装必要的库确保你已经安装了Django和DjangoRESTframework:pipinstalldjangodjangorestframework配置Django项目在myproj......
  • Vue3实战笔记(43)—Vue3组合式API下封装可复用ECharts图表组件
    文章目录前言一、封装echart图标钩子二、使用步骤总结前言接上文,已经安装好了ECharts,开始封装组件方便使用。一、封装echart图标钩子首先应用我们之前学习的钩子方式,在hooks目录下创建一个名为useECharts.js的文件,用于封装ECharts的逻辑:import{ref,onMo......
  • Vue 3指令与事件处理
    title:Vue3指令与事件处理date:2024/5/2518:53:37updated:2024/5/2518:53:37categories:前端开发tags:Vue3基础指令详解事件处理高级事件实战案例最佳实践性能优化第1章Vue3基础1.1Vue3简介Vue3是一个由尤雨溪(尤大)领导的开源JavaScript框架,它专......
  • Vue3实战笔记(40)—组件逻辑复用:自定义Hooks的完全指南
    文章目录前言一、状态管理二、副作用处理三、生命周期钩子总结前言自定义Hooks是Vue3中的一个重要特性,它允许您创建可重用的函数,以便在组件之间共享状态和逻辑。以下是一些关于自定义Hooks的常见用法。一、状态管理使用reactive或ref来创建响应式数据,并在组件中......
  • vue的生命周期
    Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载DOM、渲染、更新、卸载等一系列过程。Vue提供了一系列生命周期钩子(或称为生命周期方法),允许我们在不同阶段添加自己的代码。以下是Vue组件的主要生命周期钩子:1.**创建阶段(Creation):**  -`be......
  • vue指令
    内容渲染指令v-html 指令用于将包含HTML代码的字符串作为HTML插入到元素中,从而实现动态渲染HTML内容。条件渲染指令v-show和v-if区别及其使用场景v-show1.作用:控制元素显示隐藏2.语法:v-show="表达式"表达式值true显示,false隐藏3.原理:切换display:none控......
  • Vue3——Pinia+Pinia持久化
    Pinia安装Pinianpminstallpinia创建一个pinia实例(根store)并将其传递给应用//store/index.ts//仓库大仓库import{createPinia}from"pinia";//创建大仓库constpinia=createPinia();//对外暴露:入口文件需要安装仓库exportdefaultpinia;//mai......
  • C#串口通讯 源码Demo
    在C#中进行串口通讯主要涉及到以下几个步骤:引入命名空间usingSystem.IO.Ports;创建SerialPort对象SerialPortport=newSerialPort();设置串口属性//设置串口名:port.PortName="COM1";//设置波特率:port.BaudRate=9600;//设置校验位:port.Parity=Parity.None;//......
  • 基于springboot+vue的招聘信息管理系统
    开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:Maven3.3.9系统展示系统首页企业招聘界面求职信息界面社区留言界面个人中心管理员登录管理员功能界面用户管理......