前语
最近在作业完成一个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);
}
}
结果失败:
报错显现postMessage
履行失败,并且提示我源不同。这让我很费解,我只能理解为像天猫这样的成熟的网址是做了相应的保护措施的。
参阅资料