文章目录
- iframe
- 一、iframe的优缺点
- (一)优点
- (二)缺点
- 二、iframe基础应用
- (一)基本语法
- (二)在实际场景中的应用
- 三、iframe在无界微前端中的应用解析
- (一)沙箱隔离
- (二)沙箱穿透
- 四、无界微前端中 子应用全局弹窗应用方式
- 五、无界微前端中 全局状态管理
- 总结
- 一、iframe基础参数
- (一)src属性
- (二)width和height属性
- (三)frameborder属性
- (四)scrolling属性
- (五)name属性
- 二、iframe应用技巧
- (一)嵌入外部内容
- (二)实现页面局部更新
- (三)利用iframe隔离性
iframe
一、iframe的优缺点
(一)优点
- 隔离性强
- iframe提供了天然的隔离环境。它内部的JavaScript变量、函数和CSS样式与外部页面相互独立。例如,在一个包含多个广告模块的新闻网站中,每个广告模块都可以作为一个iframe嵌入。这样,广告脚本中的变量定义(如计数器变量用于统计广告展示次数)不会与新闻页面本身的脚本变量产生冲突,有效避免了命名空间污染。
- 简单的嵌入方式
- 可以很方便地将外部网页或内容嵌入到当前页面。只需使用
<iframe>
标签,并通过src
属性指定要嵌入内容的URL即可。比如,要在一个公司内部的知识库页面嵌入一个在线文档编辑器,用于团队成员协作编辑文档,就可以通过<iframe src="https://editor.example.com"></iframe>
这样的方式轻松实现。
- 可以很方便地将外部网页或内容嵌入到当前页面。只需使用
- 跨域资源嵌入方便
- 对于需要嵌入来自不同域名的内容(如第三方的地图服务、社交媒体分享按钮等),iframe是一种很好的解决方案。由于浏览器的同源策略限制,在不使用复杂的跨域技术的情况下,通过iframe可以相对容易地展示跨域内容。例如,嵌入一个谷歌地图的iframe(
<iframe src="https://www.google.com/maps/embed?parameters"></iframe>
)来为用户提供位置信息。
- 对于需要嵌入来自不同域名的内容(如第三方的地图服务、社交媒体分享按钮等),iframe是一种很好的解决方案。由于浏览器的同源策略限制,在不使用复杂的跨域技术的情况下,通过iframe可以相对容易地展示跨域内容。例如,嵌入一个谷歌地图的iframe(
(二)缺点
- 性能开销
- 每个iframe都相当于在主页面中开启了一个新的浏览器上下文,需要额外的资源加载和渲染。这包括HTML、CSS和JavaScript文件等。如果页面中有多个iframe,会显著增加页面的加载时间和内存占用。例如,一个包含多个视频iframe的网页,每个视频都需要加载自己的播放脚本和视频文件,会使浏览器不堪重负,导致页面加载缓慢甚至卡顿。
- SEO问题
- 搜索引擎爬虫在处理iframe内容时可能存在困难。爬虫可能不会深入到iframe内部去索引其中的内容,这会导致被嵌入的重要信息(如产品详情、文章内容等)无法被搜索引擎有效收录,影响网站的搜索排名。例如,一个电商网站如果将产品评价模块放在iframe中,这些评价内容可能无法被搜索引擎很好地索引,从而降低产品页面在搜索结果中的可见性。
- 布局和样式控制复杂
- 确保iframe在不同的设备屏幕尺寸和浏览器下都能正确显示是具有挑战性的。iframe的大小、滚动条行为等可能会受到外部页面和自身内容的双重影响。例如,在响应式设计的网页中,要让iframe中的地图和外部的文本内容协调显示,需要花费额外的精力来调整样式和布局,如设置合适的宽度、高度和边距等。
二、iframe基础应用
(一)基本语法
- 嵌入网页
- 使用
<iframe>
标签,通过src
属性指定要嵌入的网页地址。例如:
<iframe src="https://www.example.com"></iframe>
- 可以通过
width
和height
属性来控制iframe的尺寸。例如:
<iframe src="https://www.example.com" width="500px" height="300px"></iframe>
- 使用
- 设置边框和其他属性
frameborder
属性用于设置是否显示边框,0
表示不显示边框,1
表示显示边框。例如:
<iframe src="https://www.example.com" frameborder="0"></iframe>
- 还可以设置
scrolling
属性来控制是否显示滚动条,auto
(默认)表示根据内容自动显示滚动条,yes
表示始终显示滚动条,no
表示不显示滚动条。例如:
<iframe src="https://www.example.com" scrolling="no"></iframe>
(二)在实际场景中的应用
- 嵌入多媒体内容
- 可以嵌入视频或音频。例如,嵌入一个YouTube视频:
<iframe width="560" height="315" src="https://www.youtube.com/embed/VIDEO_ID" ></iframe>
- 其中
VIDEO_ID
是YouTube视频的唯一标识符。
- 嵌入外部工具或小部件
- 如嵌入一个在线表单工具。假设表单工具的网址是
https://form.example.com
,可以这样嵌入:
<iframe src="https://form.example.com" width="100%" height="500px"></iframe>
- 如嵌入一个在线表单工具。假设表单工具的网址是
三、iframe在无界微前端中的应用解析
(一)沙箱隔离
- 基于浏览器原生机制的隔离
- iframe本身就提供了一种天然的沙箱环境。在无界微前端中,每个微应用被包装在一个iframe中,其JavaScript环境是相互独立的。这意味着不同微应用中的全局变量、函数等不会相互干扰。例如,微应用A中的
window.myVariable
和微应用B中的window.myVariable
即使名称相同,在各自的iframe中也是相互独立的,就像两个不同房间里的同名物品,彼此不会产生混淆。
- iframe本身就提供了一种天然的沙箱环境。在无界微前端中,每个微应用被包装在一个iframe中,其JavaScript环境是相互独立的。这意味着不同微应用中的全局变量、函数等不会相互干扰。例如,微应用A中的
- 样式隔离
- iframe内部的CSS样式也与外部页面隔离。这样,微应用可以定义自己的样式规则而不用担心影响其他微应用。例如,微应用C使用了一种独特的按钮样式(如红色背景的按钮),通过iframe包装后,这种样式不会泄露到其他微应用中,确保了每个微应用的样式独立性。
- 安全隔离
- 从安全角度看,由于iframe的隔离性,恶意代码在一个微应用的iframe中很难突破边界去攻击其他微应用。例如,如果一个微应用被恶意注入了一段脚本试图修改其他微应用的DOM(文档对象模型)结构,在iframe的隔离下,这种攻击行为会被限制在该iframe内部。
(二)沙箱穿透
- 通信机制 - postMessage
- 在无界微前端中,虽然iframe提供了隔离,但有时微应用之间需要进行通信。这时可以使用
postMessage
API。主应用可以通过postMessage
向iframe中的微应用发送消息。例如,主应用有一个全局的用户登录状态变化,需要通知各个微应用。主应用可以这样发送消息:
const iframe = document.getElementById('microAppIframe'); iframe.contentWindow.postMessage({type: 'userLoggedIn', data: userData}, '*');
- 其中
microAppIframe
是嵌入微应用的iframe的ID,userData
是包含用户登录相关信息的数据。 - 微应用在iframe内部可以通过监听
message
事件来接收消息:
window.addEventListener('message', function(event) {if (event.data.type === 'userLoggedIn') {// 根据用户登录信息更新微应用状态updateMicroAppState(event.data.data);} });
- 在无界微前端中,虽然iframe提供了隔离,但有时微应用之间需要进行通信。这时可以使用
- 共享数据接口
- 可以通过定义共享的数据接口来实现沙箱穿透。例如,在主应用和微应用之间约定一个共享的存储对象,如
window.sharedData
。在安全可控的情况下,微应用可以通过这个共享对象来获取或修改一些全局相关的数据。不过这种方式需要谨慎处理,以确保数据的安全性和一致性,避免数据的滥用和冲突。
- 可以通过定义共享的数据接口来实现沙箱穿透。例如,在主应用和微应用之间约定一个共享的存储对象,如
四、无界微前端中 子应用全局弹窗应用方式
- 通信机制实现全局弹窗
- 使用postMessage进行通信
- 在无界微前端架构中,主应用和子应用通常是相互隔离的。当子应用需要弹出一个全局生效的窗口时,可以利用
postMessage
进行通信。子应用通过window.parent.postMessage
向主应用发送消息,告知主应用需要弹出窗口的相关信息,如窗口类型(是提示框、确认框还是自定义弹窗等)、窗口内容(文本信息、样式信息等)。 - 例如,子应用中有一个按钮点击事件触发弹窗,代码可以这样写:
document.getElementById('showPopupButton').addEventListener('click', function () {const popupData = {type: 'alert',message: '这是一个来自子应用的全局弹窗消息'};window.parent.postMessage(popupData, '*'); });
- 在无界微前端架构中,主应用和子应用通常是相互隔离的。当子应用需要弹出一个全局生效的窗口时,可以利用
- 主应用接收并处理消息
- 主应用需要监听
message
事件来接收子应用发送的消息。当接收到消息后,根据消息中的内容来创建和显示弹窗。如果是简单的alert
弹窗,主应用可以直接使用window.alert
函数。对于更复杂的自定义弹窗,可以通过操作主应用的DOM来创建和显示。 - 例如,主应用中的消息监听代码如下:
window.addEventListener('message', function (event) {if (event.data.type === 'alert') {window.alert(event.data.message);} else if (event.data.type === 'customPopup') {// 假设创建一个自定义弹窗的函数为createCustomPopupcreateCustomPopup(event.data);} });
- 主应用需要监听
- 使用postMessage进行通信
- 共享状态管理实现全局弹窗
- 使用全局状态管理库
- 可以引入像Redux、Mobx等全局状态管理库。在无界微前端架构中,主应用和子应用都可以访问和修改这个全局状态。当子应用需要弹出全局窗口时,它可以通过派发一个全局状态更新的动作。
- 例如,在使用Redux的情况下,子应用可以这样触发全局弹窗动作:
import { store } from '../mainApp/store'; document.getElementById('showPopupButton').addEventListener('click',function () {store.dispatch({type: 'SHOW_GLOBAL_POPUP',payload: {type: 'alert',message: '这是一个来自子应用的全局弹窗消息'}}); });
- 主应用根据状态变化显示弹窗
- 主应用中会有相应的Redux中间件或者监听器来处理状态变化。当收到
SHOW_GLOBAL_POPUP
这个动作时,主应用根据动作的payload
来创建和显示弹窗,就像在通信机制中处理消息一样,根据弹窗类型选择合适的显示方式。
- 主应用中会有相应的Redux中间件或者监听器来处理状态变化。当收到
- 使用全局状态管理库
- 事件总线实现全局弹窗
- 创建事件总线
- 在无界微前端架构中可以建立一个事件总线,它可以是一个简单的JavaScript对象,具有
on
(用于监听事件)、emit
(用于触发事件)等功能。主应用和子应用都可以访问这个事件总线。 - 例如,事件总线的简单实现如下:
const eventBus = {events: {},on: function (eventName, callback) {if (!this.events[eventName]) {this.events[eventName] = [];}this.events[eventName].push(callback);},emit: function (eventName, data) {if (this.events[eventName]) {this.events[eventName].forEach(callback => {callback(data);});}} };
- 在无界微前端架构中可以建立一个事件总线,它可以是一个简单的JavaScript对象,具有
- 子应用触发和主应用响应事件
- 子应用在需要弹出全局窗口时,通过事件总线的
emit
方法触发一个showGlobalPopup
事件,并传递弹窗相关的数据。主应用在初始化时通过事件总线的on
方法监听showGlobalPopup
事件,当事件被触发时,根据数据显示弹窗。 - 子应用中的触发代码:
import { eventBus } from '../common/eventBus'; document.getElementById('showPopupButton').addEventListener('click', function () {const popupData = {type: 'alert',message: '这是一个来自子应用的全局弹窗消息'};eventBus.emit('showGlobalPopup', popupData); });
- 主应用中的监听代码:
import { eventBus } from '../common/eventBus'; eventBus.on('showGlobalPopup', function (popupData) {if (popupData.type === 'alert') {window.alert(popupData.message);} else if (popupData.type === 'customPopup') {// 假设创建一个自定义弹窗的函数为createCustomPopupcreateCustomPopup(popupData);} });
- 子应用在需要弹出全局窗口时,通过事件总线的
- 创建事件总线
五、无界微前端中 全局状态管理
- 使用全局状态管理库(以Redux为例)
- 主应用中配置Redux
- 安装和创建store:在主应用中,首先安装Redux相关库(如
redux
和react - redux
,如果是基于React构建)。然后创建一个Redux store,这个store将用于存储全局状态。例如,创建一个简单的计数器状态:
import { createStore } from 'redux'; const initialState = {counter: 0 }; function reducer(state = initialState, action) {switch (action.type) {case 'INCREMENT':return {...state, counter: state.counter + 1 };case 'DECREMENT':return {...state, counter: state.counter - 1 };default:return state;} } const store = createStore(reducer);
- 将store挂载到全局对象(可选):为了方便子应用访问,可以将store挂载到
window
对象上,但这种方式可能会有一些潜在的安全风险和全局变量污染问题。更安全的做法是通过特定的接口来传递store。例如,在一个简单的场景下,可以这样挂载:
window.store = store;
- 安装和创建store:在主应用中,首先安装Redux相关库(如
- 子应用访问和修改状态
- 连接子应用到store:子应用可以通过导入主应用的store或者通过接口获取store来连接到全局状态。例如,在一个React子应用中,可以使用
react - redux
的connect
函数来连接组件到store。假设子应用中有一个组件需要显示和更新计数器的值:
import React from 'react'; import { connect } from 'react - redux'; function CounterComponent(props) {return (<div><p>Counter: {props.counter}</p><button onClick={() => props.increment()}>Increment</button><button onClick={() => props.decrement()}>Decrement</button></div>); } const mapStateToProps = state => ({counter: state.counter }); const mapDispatchToProps = dispatch => ({increment: () => dispatch({ type: 'INCREMENT' }),decrement: () => dispatch({ type: 'DECREMENT' }) }); export default connect(mapStateToProps, mapDispatchToProps) (CounterComponent);
- 连接子应用到store:子应用可以通过导入主应用的store或者通过接口获取store来连接到全局状态。例如,在一个React子应用中,可以使用
- 主应用中配置Redux
- 基于自定义事件实现全局状态管理
- 创建事件中心
- 在主应用中创建一个事件中心,用于管理状态变化事件。这个事件中心可以是一个简单的JavaScript对象,具有发布和订阅功能。例如:
const eventCenter = {events: {},subscribe: function (eventName, callback) {if (!this.events[eventName]) {this.events[eventName] = [];}this.events[eventName].push(callback);},publish: function (eventName, data) {if (this.events[eventName]) {this.events[eventName].forEach(callback => {callback(data);});}} };
- 管理全局状态
- 定义一些全局状态变量和操作这些变量的函数。例如,创建一个全局的用户登录状态变量和更新登录状态的函数:
let isUserLoggedIn = false; function updateLoginStatus(status) {isUserLoggedIn = status;eventCenter.publish('loginStatusChanged', isUserLoggedIn); }
- 子应用订阅和响应状态变化
- 子应用可以通过订阅事件中心的事件来获取全局状态的变化。例如,在一个子应用中,当用户登录状态改变时,更新界面显示:
eventCenter.subscribe('loginStatusChanged', function (isLoggedIn) {if (isLoggedIn) {// 显示用户相关界面} else {// 显示未登录界面} });
- 创建事件中心
- 利用iframe通信机制(如postMessage)实现全局状态共享
- 主应用状态管理和消息发送
- 主应用维护全局状态,当状态发生变化时,通过
postMessage
向所有子应用发送状态更新消息。例如,主应用中有一个主题切换状态,当主题改变时:
let currentTheme = 'light'; function changeTheme(newTheme) {currentTheme = newTheme;const allIframes = document.getElementsByTagName('iframe');for (let i = 0; i < allIframes.length; i++) {const iframe = allIframes[i];iframe.contentWindow.postMessage({type: 'themeChanged',theme: currentTheme}, '*');} }
- 主应用维护全局状态,当状态发生变化时,通过
- 子应用接收和更新状态
- 子应用在
window
对象上监听message
事件,接收主应用发送的状态更新消息,并更新自己的状态或界面。例如:
window.addEventListener('message', function (event) {if (event.data.type === 'themeChanged') {// 根据新主题更新子应用的样式或状态updateSubAppTheme(event.data.theme);} });
- 子应用在
- 主应用状态管理和消息发送
总结
一、iframe基础参数
(一)src属性
- 功能:用于指定要在iframe中显示的文档的URL(Uniform Resource Locator)。这是嵌入外部内容的关键参数。例如,
src="https://www.example.com"
会在iframe中加载该网址对应的网页内容。 - 应用场景:可以嵌入各种网页、在线文档、多媒体文件(如视频、音频等)。比如,在新闻网站嵌入外部新闻源,或者在企业官网嵌入第三方的产品介绍页面。
(二)width和height属性
- 功能:分别用于控制iframe的宽度和高度。可以使用像素(px)、百分比(%)等单位来设置尺寸。例如,
width="500px" height="300px"
将创建一个宽500像素、高300像素的iframe;width="80%" height="70%"
则会使iframe的宽度和高度分别占据父容器的80%和70%。 - 应用场景:根据页面布局和设计需求,精确调整iframe的大小,以适应不同的内容展示和屏幕尺寸。在响应式设计中,使用百分比单位可以让iframe随着父容器的大小变化而自适应。
(三)frameborder属性
- 功能:用于设置iframe边框的显示。
0
表示不显示边框,1
(或其他非零值)表示显示边框。例如,frameborder="0"
可以创建一个无边框的iframe,使嵌入的内容与页面其他部分在视觉上更加融合。 - 应用场景:在需要无缝嵌入内容,或者当页面整体风格要求简洁、无多余边框元素时,将
frameborder
设置为0
。而在需要明确区分嵌入内容和主页面时,可以显示边框。
(四)scrolling属性
- 功能:用于控制iframe是否显示滚动条。
auto
(默认值)表示根据iframe内部内容的高度和宽度自动决定是否显示滚动条;yes
表示始终显示滚动条;no
表示不显示滚动条。例如,scrolling="no"
可以隐藏滚动条,让内容在固定的iframe尺寸内显示,可能会导致部分内容无法看到。 - 应用场景:当嵌入的内容尺寸固定且已知可以完整显示在iframe内时,可设置
scrolling="no"
来保持页面简洁。若无法确定内容尺寸或者希望用户能方便地查看全部内容,则可以使用auto
。
(五)name属性
- 功能:为iframe指定一个名称,这个名称主要用于在表单提交或者通过JavaScript进行链接跳转时作为目标引用。例如,
name="myIframe"
,在一个表单中有一个提交按钮,设置target="myIframe"
时,表单提交后的结果将在该名称的iframe中显示。 - 应用场景:在涉及多个iframe和表单交互的复杂页面布局中,通过
name
属性可以精确控制表单提交后的内容显示位置,或者通过JavaScript代码中的window.open
等方法指定链接在特定名称的iframe中打开。
二、iframe应用技巧
(一)嵌入外部内容
- 跨域嵌入:iframe可以方便地嵌入来自其他域名的内容。在嵌入第三方内容(如社交媒体分享按钮、地图服务等)时,只需将其提供的嵌入代码(通常是包含iframe的HTML片段)复制到自己的页面中即可。例如,嵌入谷歌地图:
<iframe src="https://www.google.com/maps/embed?parameters"
width="600" height="450"></iframe>
- 嵌入多媒体:用于嵌入视频和音频内容。许多视频平台(如YouTube、Vimeo)提供了iframe嵌入代码,方便在其他网站展示视频。以YouTube为例,嵌入视频的基本代码格式如下:
<iframe width="560" height="315"
src="https://www.youtube.com/embed/VIDEO_ID"></iframe>
其中VIDEO_ID
是YouTube视频的唯一标识符。
(二)实现页面局部更新
- 独立加载内容:将页面中更新频繁或者加载速度较慢的部分放在iframe中,这样可以使这部分内容独立于主页面进行加载和更新。例如,在一个包含实时数据图表的网页中,将图表部分放在iframe中,当数据更新时,只需更新iframe中的内容,而不会影响主页面的其他部分。
- 异步加载技巧:可以通过JavaScript动态创建和加载iframe,实现异步加载内容。例如:
const iframe = document.createElement('iframe');
iframe.src = 'https://www.example.com';
iframe.width = '500px';
iframe.height = '300px';
document.body.appendChild(iframe);
这样可以在页面需要的时候(如用户点击某个按钮后)再加载iframe中的内容,提高页面初始加载速度。
(三)利用iframe隔离性
- CSS样式隔离:iframe内部的CSS样式不会影响外部页面,反之亦然。这在整合多个不同风格的内容或模块时非常有用。例如,在一个包含多个广告模块的页面中,每个广告模块作为一个iframe嵌入,广告模块的样式不会干扰主页面的布局和样式。
- JavaScript隔离:iframe中的JavaScript脚本在独立的执行环境中运行,变量和函数与外部页面隔离。这可以避免不同脚本之间的变量冲突和命名空间污染。例如,在一个复杂的网页应用中,不同的功能模块(如用户登录模块、评论模块等)分别放在不同的iframe中,它们内部的JavaScript代码可以独立开发和维护,不用担心相互干扰。