前语

最近在作业完成一个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方针的办法有三种:

  1. iframe节点方针 的 contentWindow 特点
  2. 履行window.open所回来的窗口方针
  3. 运用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()办法接纳三个参数:

  1. message,需求传递的音讯,这个参数最初被规划为只能传递字符串,后被改为能够传任何类型的数据,但是并非一切浏览器都支持这一改动,所以建议仍是最好只传字符串。
  2. targetOrigin,方针页面的URL , 能够是 * 表明无限制,也能够是一个URL
  3. transfer,可选的可传输方针的数组(只与作业线程相关)

当接纳到postMessage发送的信息后,window方针上会触发message事情。message事情的event包含以下的内容:

  1. data,postMessage第一个参数传递的数据
  2. origin,发送信息的文档源
  3. 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履行失败,并且提示我源不同。这让我很费解,我只能理解为像天猫这样的成熟的网址是做了相应的保护措施的。


参阅资料

  1. 前端HTML网页之间传递数据多种办法,附代码事例_html传值给另一个html-CSDN博客
  2. vue组件向HTML文件传送数据,HTML向vue组件发送数据。vue组件与HTML文件间进行交互的办法。_html与vue通信-CSDN博客
  3. Vue中嵌入html页面并彼此通信_AKA皮卡Q-华为云开发者联盟
  4. js处理url传递中文参数乱码问题_js url 中文乱码-CSDN博客
  5. window.postMessage – Web API 接口参阅 | MDN