iframe动态操作标签分享

news2024/9/20 12:32:46

前言

分享一个近期工作中遇到的关于IFrame的需求,以及解决方案。

需求大致是说在我们系统中嵌套了另一个文档页面,这个文档页面是爬取的,并且页面是原先使用后端渲染实现的,取到的css和script标签都是相对路径比如: "./mian.css" 这种,这么写会导致当origin发生变化时取不到静态资源,怎么解决这个问题呢?

解决方案思考

使用Nginx做反向代理

配置Nginx反向代理,在Nginx配置中添加一个代理规则,将请求定向到目标文档页面的地址。 

后端动态修改页面路径

将相对路径改为绝对路径,通过解析文档页面,找到其中的相对路径资源引用,将其改为绝对路径。这样不受origin变化的影响。

前端代理(只能在dev环境下实现)

使用vite的proxy实现反向代理效果

先说结论,上述三种方式均被pass了

第一种方法由于页面请求到了宿主的网址下,导致Nginx监听不到资源请求,再有是前端想配置Nginx并不容易。。。

第二种缺乏可复用性,如果宿主的origin发送变化,则后端规则也要跟着改变

第三种就更不用说了,只能在开发环境下实现,不过这种方式给了我一定的启发,如果将静态资源打包进正式包里,再动态修改IFrame的资源路径,或许可以解决相关问题

设计概要

有了方案就需要技术的实施,总共有两步:

第一步是将静态资源打包进正式包中,这里可以使用rollup的插件rollup-plugin-copy来达到复制静态资源的目的

第二步是动态修改iframe中的link标签的href地址,达到资源替换的效果

方案实现

静态资源打包

和webpack有些不同,webpack可以通过CopyWebpackPlugin或者IgnorePlugin等方式复制或排除文件,而使用vite则需要借助其他plugs工具实现,比如vite-plugin-cp或者vite-plugin-static-copy,然而事情并没有这么简单,由于项目环境的复杂性较高,在esm和cjs上发生了错误,有些包是以esm导入的,但是项目中什么文件都有,无法兼顾既要又要,就像下面这样:

为了尽量不改变项目结构,我决定自己造轮子,自己写个插件,在vite的closeBundle生命时将上面要用到的静态文件复制到dist文件夹下

在项目根目录下新建script文件夹,创建新的脚本

其中helpers是工具函数

const noop = (_ = {}) => {}
export const defer = () => {
  let resolve = noop,
    reject = noop
  const promise = new Promise((_resolve, _reject) => {
    resolve = _resolve
    reject = _reject
  })
  return { promise, resolve, reject }
}

接着实现一下复制文件夹的node脚本

import fs from 'fs-extra'
import { defer } from './helpers.js'
// 手动复制文件夹
export const copyFile = async (
  source,
  target,
  config = { overwrite: true },
) => {
  const { promise, resolve, reject } = defer()
  fs.copy(source, target, config, (err) => {
    if (err) {
      reject(err)
      console.error('复制出错', err)
    } else {
      resolve()
      console.log('复制成功!')
    }
  })
  return promise
}

然后实现一下vite插件的hook函数,需要注意的是,在我的脚本之前有个打包zip的插件,为了在zip打包之前进行复制静态文件操作,我做了个异步响应操作

import { copyFile } from './copyFile.js'

export const copyStaticAfterBuild = (opts, cb) => {
  return {
    name: 'copy-static-after-build',
    closeBundle() {
      const taskers = opts.map((item) => {
        console.log(`copy static ${item.src} to ${item.dest}`)
        return copyFile(item.src, item.dest, { overwrite: true })
      })
      return Promise.all(taskers).then(cb)
    },
  }
}

最后是在vite.config中使用

import { defineConfig } from 'vite'

import { zipAfterBuild, copyStaticAfterBuild } from "./scripts"


// https://vitejs.dev/config/
export default defineConfig(({ command }) => {
    return {
        plugins: [
            copyStaticAfterBuild([
                {
                    src: './static',
                    dest: './dist/static'
                }
            ], zipAfterBuild({}).closeBundle),
        ],
    }
})

实现效果就是下面这样的

打包后的效果

iframe的通信及标签动态修改

参考之前写的博客,我们可以取iframe的标签并对其dom进行操作,这里我是在react中进行操作,所以写个ref获取标签,其中我们通过iframe.contentWindow.document获取到iframe的dom对象,然后对其内容进行修改,由于操作的步骤不多,使用ipc反而会增加代码量,完整的代码如下

import { useLocation } from 'react-router-dom'
import './detail.scss'
import { useCallback, useEffect, useRef } from 'react'

// 定义 LinksType 类型,可以是 HTMLLinkElement 或 HTMLScriptElement
type LinksType = HTMLLinkElement | HTMLScriptElement

// 定义网址替换规则的接口 IRule
type IRule = {
  source: string // 源网址
  target: string // 目标网址
}

// 定义替换网址参数的接口 ParamsOfReplaceUrl
interface ParamsOfReplaceUrl<T extends LinksType> {
  links: NodeListOf<T> // 标签列表,可以是 link、a 等等
  rules: IRule[] // 网址替换规则,全字匹配
}

// 获取当前页面的基础网址
const base = `${window.location.origin}/`

// 定义默认的替换规则数组
const rules = [
  { source: base + 'static/css/', target: base + 'static/' }, // 替换 CSS 资源的规则
  {
    source: base + 'static/components/bootstrap-4.3.1/css/', // Bootstrap CSS 资源的规则
    target: base + 'static/', // 替换目标
  },
]

/**
 * 批量替换Href网址
 * @param links 标签列表,link、a 等等
 * @param rules 网址替换规则,全字匹配
 */
const replaceHrefUrl = <T extends HTMLLinkElement>({
  links,
  rules,
}: ParamsOfReplaceUrl<T>) => {
  links.forEach((link) =>
    rules.forEach((rule) => {
      link.href.includes(rule.source) &&
        (link.href = link.href?.replace?.(rule.source, rule.target))
    }),
  )
}

/**
 * 批量替换Src网址
 * @param links 标签列表,img、video、script 等等
 * @param rules 网址替换规则,全字匹配
 */
const replaceSrcUrl = <T extends HTMLScriptElement>({
  links,
  rules,
}: ParamsOfReplaceUrl<T>) => {
  links.forEach((link) =>
    rules.find((rule) => {
      if (rule.source.includes(link.src)) {
        const newLink = document.createElement('script')
        newLink.src = link.src?.replace?.(rule.source, rule.target)
        link?.parentNode?.replaceChild(newLink, link)
      }
    }),
  )
}

// IntelDetail 组件
const IntelDetail = () => {
  const location = useLocation()
  const iframe = useRef<HTMLIFrameElement>(null)

  // 加载处理程序
  const loadHandler = useCallback(() => {
    const elem = iframe?.current
    const scriptSrc = elem?.contentWindow?.document.querySelectorAll('script')
    const cssLink = elem?.contentWindow?.document.querySelectorAll('link')

    if (cssLink) {
      replaceHrefUrl({ links: cssLink, rules }) // 替换 CSS 链接
    }
    if (scriptSrc) {
      replaceSrcUrl({ links: scriptSrc, rules }) // 替换 Script 资源
    }
  }, [])

  // 组件加载时执行加载处理程序,并在组件卸载时清理事件监听器
  useEffect(() => {
    const elem = iframe?.current
    elem?.addEventListener('load', loadHandler)
    return () => {
      elem?.removeEventListener('load', loadHandler)
    }
  }, [])

  // 渲染组件
  return (
    <div className="intel-detail">
      <iframe
        ref={iframe}
        src={`${window.location.origin}/jaguar/vul_intelligence/content_s/${location.state.id}`}
        width="100%"
        height="100%"
        frameBorder={0}
      ></iframe>
    </div>
  )
}

export default IntelDetail

上述代码中,我们将iframe中的相对资源路径改到了项目的./static中,修复了资源缺失的问题。

效果展示

在使用了上述方案对项目打包后,之前的资源未找到的问题也被解决,效果如下

可以看到在iframe中首先会加载两次资源,在未取到资源后,又会重新获取两个css资源,随后DOM树和CSS树发生重排操作重新渲染,虽然有比较明显的样式过渡,但是这已经是目前最佳的解决方案了

写在最后

本文分享了通过打包静态资源并动态替换 iframe 中的资源路径,成功解决了因 origin 变化导致的资源加载问题。这种方案也让我想起了原先写的:基于内网穿透+Fiddler的私有化项目调试前端解决方案_fiddler onbeforerequest-CSDN博客

二者在实现方案上不太相同,但是思路还是有相似之处的

以上就是文章全部内容了,感谢你看到了最后,如果觉得文章不错的话,还望三连支持一下,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1532816.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【文件操作和IO】

文件操作和IO 1.文件2. 硬盘上文件的目录结构3. 文件路径4. 文件重要分类&#xff1a;5. Java中操作文件5.1 Java对于文件操作的API5.2 Java中使用File类来进行文件操作5.3 File类属性5.4 构造方法5.5 方法&#xff1a; 6. 文件内容的读写 -- 文件流&#xff08;数据流&#xf…

32.768K晶振X1A000141000300适用于无人驾驶汽车电子设备

科技的发展带动电子元器件的发展电子元器件-“晶振”为现代的科技带来了巨大的贡献&#xff0c;用小小的身体发挥着大大的能量。 近两年无人驾驶汽车热度很高&#xff0c;不少汽车巨头都已入局。但这项技术的难度不小&#xff0c;相信在未来几年里&#xff0c;无人驾驶汽车这项…

改进粒子群优化算法||粒子群算法变体||Improved particle swarm optimization algorithm

粒子群算法&#xff08;Particle Swarm Optimization&#xff0c;PSO&#xff09;是一种基于群体智能的优化算法&#xff0c;其思想来源于鸟群寻食和鱼群捕食等自然现象。PSO算法通过模拟群体智能的行为&#xff0c;以一种启发式的方式寻找最优解&#xff0c;因此具有全局搜索能…

数学建模(熵权法 python代码 例子)

目录 介绍&#xff1a; 模板&#xff1a; 例子&#xff1a;择偶 极小型指标转化为极大型&#xff08;正向化&#xff09;&#xff1a; 中间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 区间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a…

【Web应用技术基础】HTML(4)——表单类的标签

目录 题目1&#xff1a;文本框 题目2&#xff1a;密码框 题目3&#xff1a;单选框 题目4&#xff1a;多选框 题目5&#xff1a;单选框选中 题目6&#xff1a;禁用disabled 题目7&#xff1a;lable标签 题目8&#xff1a;下拉框 题目9&#xff1a;textarea 题目10&…

【Java之老话常谈】学习Java可以用来做什么?

对于很多新手来说,刚开始接触Java会很迷惘,不知道Java可以做什么。其实Java 可以做的东西太多了,手机游戏、中间件、软件、网站,电脑游戏,以及现在流行的安卓手机app等,都是由java语言编写的。由于Java拥有很高的安全性、平台移植性等,所以受到广大程序员的喜爱。 java…

接口测试、postman、测试点提取【主】

接口测试是测试系统组件间接口的一种测试 接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点 测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系 文章目录 HTTP接口 & Web Service接口RESTful接口…

ubuntu10.04 apache2.2开启tls1.2的支持,使现代的edge和firefox浏览器能正常访问https

最近发现自己ubuntu10.04服务器上的apache https无法通过win11上的edge和firefox浏览器访问&#xff0c;但xp下的ie6和ie8没有问题。 firefox的错误提示为“此网站可能不支持TLS 1.2协议,而这是Firefox支持的最低版本”。 经过检查发现&#xff1a; IE6访问https所需的版本是SS…

virtualBox镜像复制

镜像复制 有一个镜像后&#xff0c;图方便&#xff0c;想直接使用这个vdi文件&#xff0c;但vdi有个uuid值&#xff0c;同一个虚拟机中不能同时存在两个同样的uuid的介质的&#xff0c;普通的复制文件所得到的uuid是一样的 &#xff0c;所以需要用到自带的方法复制vdi文件&…

隐私计算实训营学习二:隐私计算开源如何助力数据要素流通

文章目录 一、数据要素流转与数据内外循环二、数据外循环中的信任焦虑三、数据要素流通对隐私计算的期望四、隐私计算开源助力数据要素流通 一、数据要素流转与数据内外循环 数据要素流转过程(从数据采集加工->到数据价值释放)&#xff1a; 链路主要包括采集、存储、加工、…

计算机网络面经-什么是IPv4和IPv6?

前言 Internet协议&#xff08;IP&#xff09;是为连接到Internet网络的每个设备分配的数字地址。它类似于电话号码&#xff0c;是一种独特的数字组合&#xff0c;允许用户与他人通信。IP地址主要有两个主要功能。首先&#xff0c;有了IP&#xff0c;用户能够在Internet上被识别…

JUnit5的条件测试、嵌套测试、重复测试

条件测试 JUnit5支持条件注解&#xff0c;根据布尔值判断是否执行测试。 自定义条件 EnabledIf和DisabledIf注解用来设置自定义条件&#xff0c;示例&#xff1a; Test EnabledIf("customCondition") void enabled() { // ... } Test DisabledIf("cust…

动态规划课堂6-----回文串问题

目录 引言&#xff1a; 例题1&#xff1a;回文子串 例题2&#xff1a;回文串分割IV 例题3&#xff1a;分割回文串II 例题4&#xff1a;最长回文子序列 例题5&#xff1a;让字符串成为回文串的最小插入次数 引言&#xff1a; 回文字符串 是正着读和倒过来读一样的字符串。…

Sora后时代文生视频的探索

一、写在前面 按常理&#xff0c;这里应该长篇大论地介绍一下Sora发布对各行业各方面产生的影响。不过&#xff0c;这类文章已经很多了&#xff0c;我们今天主要聊聊那些已经成熟的解决方案、那些已经可以“信手拈来”的成果&#xff0c;并以此为基础&#xff0c;看看Sora发布…

PHP全新美化广告横幅在线制作源码

源码简介 可以做网站的引流不需要安装上传就可以使用&#xff0c;在第一版基础上做了二次开发更加好用 注意&#xff1a;主机和服务器均可架设搭建,如果使用宝塔架设点击访问的时候提示找不到文件路径的时候,记得点击网站目录把防跨站攻击先关闭,这样就可以正常访问了,这款是…

用pdf2docx将PDF转换成word文档

pdf2docx是一个Python模块&#xff0c;可以将PDF文件转换为docx格式的Word文档。 pdf2docx模块基于Python的pdfminer和python-docx库开发&#xff0c;可以在Windows、Linux和Mac系统上运行。它可以从PDF文件中提取文本和图片&#xff0c;并将其转换成可编辑的Word文档&#xf…

手撕算法-二叉搜索树与双向链表

牛客BM30。 描述&#xff1a;https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId295&tqId23253&ru/exam/oj&qru/ta/format-top101/question-ranking&sourceUrl%2Fexam%2Foj分析&#xff1a;二叉搜索树的中序遍历是递增序列。可以利用…

相聚武汉氢能展_2024武汉国际氢能源及燃料电池产业博览会

相聚武汉氢能展_2024武汉国际氢能源及燃料电池产业博览会 2024武汉国际氢能源及燃料电池产业博览会 2024 Wuhan International Hydrogen Energy and Fuel Cell Industry Expo 同期举办&#xff1a;2024世界汽车制造技术暨智能装备博览会 时间&#xff1a;2024.8.14-16 地…

【动态规划】【同余前缀和】【多重背包】[推荐]2902. 和带限制的子多重集合的数目

本文涉及知识点 动态规划汇总 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 C算法&#xff1a;滑动窗口总结 多重背包 LeetCode2902. 和带限制的子多重集合的数目 给你一个下标从 0 开始的非负整数数组 nums 和两个整数 l 和 r 。 请你…

拷贝他人maven仓库jar包到自己本地仓库,加载maven依然提示无法下载对应依赖

所遇问题&#xff1a; 拷贝他人maven仓库jar包到自己本地maven仓库repository下的对应依赖位置&#xff0c;重新加载idea的maven依然提示无法下载对应依赖。 解决办法&#xff1a; 在maven->repository找到对应报错依赖路径&#xff0c;删除xxx.repositories 和 xxx.lastU…