【场景方案】我所积累的一些跨页面的数据传递方式,持续更新,欢迎补充~

Iframe内嵌相互传递

使用window.postMessage()的这个html5特性去跨域传递数据,不受跨域限制。

父层:

sendMes(){ // 向iframe发送
	let iframdom = this.$refs.iframdom // 拿到iframe的dom
	let _window = iframdom.contentWindow // 拿到iframe的window对象
	let _document = iframdom.contentDocument // 拿到iframe的document对象
	_window.postMessage({type: 'sendMes', text: '内容'}, '*') 
	// 注意点:1 第一个参数传内容用对象装,因为webpack会默认主动触发一次,
	// 即使sendMes没执行,这样被第一次触发的时候我们能通过对象的内容去区分。
	// 2 第二个参数写传入的地址是什么,如果不限制地址写*即可
}

window.addEventListener("message", (e) => { // 接收子层
	// e.data及postMessage的第一个参数,e.origin及postMessage的第二个参数
})

子层:

window.addEventListener("message", (e) => { // 接收父层
	// e.data及postMessage的第一个参数,e.origin及postMessage的第二个参数
})

sendOut(){ // 向父层发送
	window.top.postMessage({type: 'sendMes', text: '内容'}, '*') 
}

个人对这个api的看法:

  • 能够支持跨域,这样不同项目的网站都能相互通信了。
  • 当传输的内容多了,整个工程到处都是postMessage,到处监听,会非常的混乱(建议监听统一放到类似App.vue的组文件中)。
  • 父层与子层需要定好规则,例如你的项目是子层,就要去问负责开发父层网站的开发人员,怎么按照他们的规范去传递数据和获取回数据

这里推荐篇基础使用文章:【记录postMessage的详细使用
再推荐篇补充的:【终于搞懂了 Iframe (跨窗口通信)


window.postMessage()

window.postMessage()不仅用于iframe,还可以主动打开页面让后建立信息传递通道,例如页面A

let opener = null; // 保存打开窗口的引用
popBtn.onclick = function () {
    opener = window.open("B.html", "123", "height=400,width=400,top=10,resizable=yes");
}
btn.onclick = function () {
    let data = {
        value: content.value
    }
    // data 代表的是发送是数据,origin 用来限制访问来源,也可以用 * 代替
    opener.postMessage(data, "*");
}

被打开的页面B:

window.addEventListener('message', function (e) {
    console.log(e.data);
}, false);//事件监听

想这种方式我更愿意用url带参数去区分


BroadcastChannel同标签页数据传递

这个api适合同一个浏览器中,访问多个标签页,标签页之间的通信:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
    </style>
</head>

<body>
    <button id="btn">点击</button>
    <script>
        let btnDom = btn
        const channel = new BroadcastChannel('card')

        btnDom.onclick = function () {
            channel.postMessage({
                a: 1
            })
        }

        channel.onmessage = (e) => {
            console.log('监听到信息', e.data);
        }

    </script>
</body>

</html>

当a标签页点击了按钮,b标签页就能够接收到数据。兼容性也很好。

当然在框架中使用有些注意事项:

用添加监听器的方式去监听channel的信息传递,这样好销毁内存

channel.addEventListener('message', handler)
channel.removeEventListener('message', handler) // 销毁

还有一个要注意的,如果postMessage时传入的是框架里的响应式变量是会报错的,传入的时候用…处理下。

综上,最好单独写个js文件去调用BroadcastChannel:

const channel = new BroadcastChannel('demo')

export function sendMsg(type, content) {
	channel.postMessage({
        type,
        content,
    })
}

export function listenMsg(callback) {
	const handler = (e) => {
		callback && callback(e.data)
	}
	channel.addEventListener('message', handler)
	return ()=>{
		channel.removeEventListener('message', handler) // 让组件在使用的时候能够得到销毁函数,在生命周期钩子中销毁
	}
}

localStorage中间人传递

localStorage是可以跨页签的,只要是同域的都共用一个localStorage缓存,利用这个特性可以:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
    </style>
</head>

<body>
    <button id="setData">存储</button>
    <button id="getData">获取</button>
    <script>
        let setDataDom = setData
        let getDataDom = getData

        setDataDom.onclick = function () {
            localStorage.setItem('a', 1)
        }

        getDataDom.onclick = function () {
            console.log(localStorage.getItem('a'));
        }

    </script>
</body>

</html>

iframe嵌套相同域名的页面应该也是可以正常使用的,不过

  • 多占用localStorage资源
  • 无法做到主动接收

这里提个醒sessionStorage每个页签都是独立的


localStorage监听

想不到吧!localStorage也可以监听。

首先咱们写一个html文件:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>

    </style>
</head>

<body>
    <script>
        localStorage.name = 'nick'
    </script>
</body>

</html>

然后再写一个html文件去监听localStorage的修改事件:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        window.addEventListener("storage", (e) => {
            console.log(`${e.key}${e.oldValue}修改为${e.newValue}`);
            console.log(e.storageArea);
            console.log(`被改变的url为${e.url}`);
        }, true);
    </script>
</body>

</html>

在这里插入图片描述
看到了吧,监听到了。


Service Worker

这个需要一个js文件作为代理服务器,例如写一个service.js:

self.addEventListener("message",async event=>{
    const clients = await self.clients.matchAll(); // 利用浏览器收集所有的页面对象
    clients.forEach(function(client){
        client.postMessage(event.data) // 往每个页面都发送消息
    });
});

然后只需要在页面中写:

// 首先注册上面写的service.js
navigator.serviceWorker.register('service.js')
    .then(() => {
        console.log("service worker 注册成功");
    });
// navigator.serviceWorker.controller.postMessage('hello'); // 往所有页面发送消息
navigator.serviceWorker.onmessage = function ({ data }) { // 实时接收消息
    console.log(data);
}

这样不同页签就能传递数据了


Shared Worker

我感觉是独立开了一个线程去处理,好几个页面都可以共享这个线程(猜测)。

首先也是要写个中间人worker.js:

var data = '';
onconnect = function (e) {
    var port = e.ports[0]
    port.onmessage = function (e) {
        // 接收postMessage传达来的动作,是可自定义的,例如传个get,这里就可以判断,然后把存储的数据发送出去
        if (e.data === 'get') {       
            port.postMessage(data);
            data = "";
        } else {    
            // 接收postMessage传达来的数据进行存储                
            data = e.data
        }
    }
}

发送方:

const worker = new SharedWorker('worker.js')
btn.onclick = function () {
    worker.port.postMessage(content.value);
}

接收方:

var worker = new SharedWorker('worker.js');
worker.port.start()
// 这里接收worker传来的信息
worker.port.addEventListener('message', (e) => {
    if(e.data){
        console.log('来自worker的数据:', e.data)
    }
}, false);

// 这里要轮番的向worker要信息
setInterval(function(){
    // 获取和发送消息都是调用 postMessage 方法,我这里约定的是传递'get'表示获取数据。
    worker.port.postMessage('get')
},1000);

缺点就是要轮番查询…


其他方式

  • indexedDB,使用麻烦,一般不用考虑到
  • websock,使用成本高,你想用领导不想你用
  • cookie轮询,能别用cookie就别用了

很多方案其实都很优秀,只是看你的业务具体适合哪些场景

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值