前端跨页面数据传输的方法研究
原文链接:https://blog.csdn.net/m0_52912501/article/details/136085383
最近实现一个popup弹窗的过程中涉及到了跨页面的数据传递问题,由于之前开发的都是单页面应用,因此对于这方面就不太了解,于是今天便写下这篇文章,想好好探讨一下。
一、问题场景还原
1.场景模拟
首先我来还原一下当时的场景,在我的系统的地图上需要添加一个弹窗,由于所使用的GIS库的要求,所以弹窗内容是一个独立的html文件。同时弹窗(html文件)中的内容必需根据系统中的数据进行渲染,这就涉及到了如何将系统中的数据传递给弹窗html的问题。
现在我简单的模拟一下,我创建了两个html文件,其中index.html 代表系统:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>主页面</title> <style> .cl-button { width: 150px; padding: 10px; font-size: 23px; cursor: pointer; } .cl-map { display: flex; flex-direction: column; align-items: center; width: 90vw; height: 1000px; background-color: rgba(140, 140, 139, 0.3); border: 3px solid; margin-top: 40px; } </style> </head> <body> <button class="cl-button" onclick="openPopup()">打开弹窗</button> <div class="cl-map"> <h1>我是地图</h1> <div class="cl-popup"></div> </div> <script> function openPopup() { const popup = document.querySelector('.cl-popup') popup.innerHTML = createIframe(600, 300, './receiver.html') } function createIframe(width, height, url) { return `<iframe width='${width}' height='${height}' src=${url} />` } </script> </body> </html>
receiver.html代表弹窗的html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>接收页面</title> <style> html { background-color: #fff; } body { display: flex; flex-direction: column; align-items: center; } </style> </head> <body> <h2>我是弹窗</h2> <div> <b style="color: red">如何将数据传递给弹窗?</b> </div> <script></script> </body> </html>
效果如下:

2.需要实现的功能
我最终要实现的效果是从主页面中将一些数据传递给弹窗,然后弹窗使用这些数据绘制一个echarts图表。
需要传递的数据如下:
const chartData = {
name:'狮驼岭区域',
time:['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
data:[150, 230, 224, 218, 135, 147, 260]
}
二、数据传递方法
1.使用客户端存储来传递数据
(1) 思路
基本思路就是使用一种客户端存储方式,在主页面将数据存储到浏览器中,当打开弹窗时再从浏览器的存储空间里读取这些数据。
客户端存储的方式有cookie、sessionStorage、localStorage和 indexedDB ,这里为了方便我直接使用sessionStorage。
(2) 实现
数据传递:
//传递数据d的方法
function transmitData(data) {
sessionStorage.setItem('chartData', JSON.stringify(data))
}
数据接收:
let chartData
// 接收数据
function receiveData() {
chartData = JSON.parse(sessionStorage.getItem('chartData'))
}
(3) 注意点
在使用客户端存储方案的时候有两个点需要注意:
第一,客户端存储的特点是存储库都是与页面源绑定的,也就是说只有来自同一个域(子域不可以)、在相同的端口上使用相同的协议的页面才可以访问同一个存储空间。
也正是这个原因在之前我在工作中没有选择客户端存储的方案。
第二,如果使用的是Web Storage(sessionStorage和localStorage),它只能存储字符串数据,非字符串数据会被转换为字符串。
sessionStorage.setItem('value', 110)
console.log(typeof sessionStorage.getItem('value'))//String
从上面这个例子中就可以看到,我存入了一个数字,取出来了之后就变成了字符串。
因此如果存储的数据是一个对象的话我们在读取时就很难将其还原。解决方法是将数据序列化,转化为JSON格式进行存储。
const data = {
num: 110,
}
sessionStorage.setItem('value', JSON.stringify(data))
console.log(JSON.parse(sessionStorage.getItem('value')))//{num: 110}
在存储数据时使用JSON.stringify()进行序列化;在读取数据的时候使用JSON.parse()进行反序列化。
2.使用查询字符串传递数据
(1) 思路
基本思路就是利用弹窗的URL中的查询字符串传递数据。至于查询字符串,就是指URL问号之后直到末尾的内容,也就是常说的“将需要传递的数据拼在URL的后面”。
例如,我在主页面中,给弹窗页面的URL加上一段查询字符串

然后再在弹窗页面中通过location.search读取


(2) 实现
数据传递:
// 打开弹窗 function openPopup() { // 获取弹窗包装元素节点 const popup = document.querySelector('.cl-popup') // 将弹窗页面嵌入,通过查询字符串传递数据 popup.innerHTML = createIframe( 600, 300, './receiver.html?' + transmitData(chartData) ) } // 创建iframe function createIframe(width, height, url) { return `<iframe width='${width}' height='${height}' src=${url} />` } //传递数据的方法 —— 生成查询字符串 function transmitData(data) { const search = new URLSearchParams() Object.entries(data).forEach((el) => { search.append(el[0],JSON.stringify(el[1])) }) return search.toString() }
数据接收:
let chartData = {}
// 接收数据
function receiveData() {
const search = new URLSearchParams(location.search)
for (const [key, value] of search) {
chartData[key] = JSON.prase(value)
}
}
最终效果:

(3) 注意点
首先是和之前一样,也要注意将需要传递的数据进行序列化,否则在传递对象和数组的时候会出现问题。
其次是这里我使用了URLSearchParams类型来辅助我操作查询字符串,这个类接收一个查询字符作为参数,它的·实例具有以下方法可以帮助我们操纵查询字符串:
|
append() |
插入一个指定的键/值对作为新的搜索参数 |
| delete() | 删除一个指定的搜索参数 |
| entries() | 返回与搜索参数键值对二维数组相关联的迭代器 |
|
get() |
获取指定搜索参数的第一个值 |
|
getAll() |
获取指定搜索参数的所有值 |
|
has() |
判断是否存在此搜索参数 |
|
keys() |
返回与搜索参数键数组相关联的迭代器 |
|
values() |
返回与搜索参数键值数组相关联的迭代器 |
|
set() |
给一个搜索参数设置新值 |
|
sort() |
按键名排序 |
|
tostring() |
返回搜索参数组成的字符串,可直接使用在 URL 上 |
3.通过窗口window对象传递数据
(1) 思路
如果子页面使用iframe嵌入主页面的,那我们就可以尝试获取到子页面的window对象,然后直接调用子页面中的函数将数据作为函数的参数传递过去,或者直接将数据存储为子页面的全局变量。
获取到子页面window对象的方法有三种:
iframe节点对象 的 contentWindow 属性
执行window.open所返回的窗口对象
利用iframe[name]或者索引从父页面的window.frames中获取
(2)实现
可以通过window.frames获取到主页面中的所有子页面,然后通过name属性找到弹窗对应的子页面。
function openPopup() {
const popup = document.querySelector('.cl-popup')
popup.innerHTML = createIframe(600, 300, './receiver.html')
//从window.frames中找到弹窗页面的window对象
let popupWindow = window.frames['popup']
popupWindow.onload = function(){
// 调用弹窗页面的初始化方法,并将数据作为参数传入
popupWindow.initChart(chartData)
}
}
也可以从弹窗iframe元素节点对象的contentWindow属性中获取到子页面的window对象。
function openPopup() {
const popup = document.querySelector('.cl-popup')
popup.innerHTML = createIframe(600, 300, './receiver.html')
let popupWindow = document.querySelector('#popupIframe').contentWindow
popupWindow.onload = function () {
popupWindow.initChart(chartData)
}
}
4.通过postMessage传递数据
(1) postMessage简介
postMessage是window对象中的一个方法,它主要用于跨文档传递消息(不同源或不同工作线程),当然像我们模拟这种同源的页面间也可以用它来进行通讯。
tip: 如果想要给子页面传递数据,需要调用子页面window对象上的postMessage
postMessage()方法接收三个参数:
- message,需要传递的消息,这个参数最初被设计为只能传递字符串,后被改为可以传任何类型的数据,但是并非所有浏览器都支持这一改动,所以建议还是最好只传字符串。
- targetOrigin,目标页面的URL , 可以是 * 表示无限制,也可以是一个URL
- transfer,可选的可传输对象的数组(只与工作线程相关)
当接收到postMessage发送的信息后,window对象上会触发message事件。message事件的·event包含以下的内容:
- data,postMessage第一个参数传递的数据
- origin,发送信息的文档源
- source,发送信息的文档中window对象的代理,主要是为了使用其中的postMessage方法回复消息。如果是相同源的情况下source就是window对象,如果是不同源的情况source中可能只有postMessage可用。
(2) 实现
function openPopup() {
const popup = document.querySelector('.cl-popup')
popup.innerHTML = createIframe(600, 300, './receiver.html')
// 获取弹窗页面的window对象
let popupWindow = window.frames['popup']
// 向弹窗页面传递消息
popupWindow.postMessage(
JSON.stringify(chartData),
location.origin + '/博客代码/跨页面通讯的若干问题/receiver.html'
)
// 弹窗页面侦听message事件,接收参数
popupWindow.onmessage = function (event) {
if (event.source !== this.parent) return;
// 将postMessage所传递的数据加入到子页面的全局作用域中
this.echartData = JSON.parse(event.data)
}
}
(3) 疑问
看到postMessage可以解决跨域问题,所以我就萌生了一个大胆的想法,能否在我的主页面中嵌入天猫的网页,然后使用postMessage实现将数据传给天猫呢?
于是进行尝试
function openPopup() {
const popup = document.querySelector('.cl-popup')
popup.innerHTML = createIframe(
600,
300,
'https://www.tmall.com/?page_offline=true'
)
// let popupWindow = document.querySelector('iframe').contentWindow
let popupWindow = window.frames['popup']
popupWindow.postMessage(
JSON.stringify('欢迎来到天猫'),
'https://www.tmall.com/?page_offline=true'
)
popupWindow.onmessage = function (event) {
if (event.source !== this.parent) return;
// 将postMessage所传递的数据加入到子页面的全局作用域中
const message = JSON.parse(event.data)
alert(message);
}
}
结果失败:


浙公网安备 33010602011771号