iframe通过postMessage进行跨域通信以及在Angular中使用
写在前面
在前端开发过程中,会遇到一些需要使用iframe的场景,使用iframe关键的一个点是数据之间的传输,基于同源的要求十分苛刻,大家基本上是都是跨域的,如果跨域进行数据传输呢?
大家使用的比较多的就是postMessage()这个方法了,下面将具体展示如何在html中使用iframe进行数据传输,以及在angular框架中如何使用以及在angular中与html中的差异性
普通html页面
由外到内(向iframe内网页传输数据)
使用iframe处
<body>
<iframe src="./iframe-content.html" class="iframe" frameborder="0"></iframe>
<script>
const iframeElement = document.querySelector(".iframe");
//需要等待iframe加载完成后再发送信息,原因是 iframe的网页需要注册message事件,若先发消息再注册,那么在注册之前是收不到消息的
iframeElement.addEventListener("load", () => {
//相当于iframe自己给自己发消息
iframeElement.contentWindow.postMessage("这是一条信息", "*");
});
</script>
</body>
iframe内容
<body>
<span>这里是iframe内容</span>
<script>
window.addEventListener("message", (event) => {
console.log(event.data);
});
</script>
</body>
由内到外
使用iframe处
<body>
<iframe src="./iframe-content.html" class="iframe" frameborder="0"></iframe>
<script>
window.addEventListener("message", (event) => {
console.log(event.data);
});
</script>
</body>
iframe内容
<body>
<span>这里是iframe内容</span>
<script>
//给上层级发消息,若上层级是顶层可以使用window.top
window.parent.postMessage("给使用处发消息", "*");
</script>
</body>
在Angular使用
- 首先是src 在angular中直接使用src链接会被认为是不安全的,需要通过DomSanitizer中的bypassSecurityTrustResourceUrl方法进行一个转化才可使用
constructor(
private sanitizer: DomSanitizer
) {
this.src = this.sanitizer.bypassSecurityTrustResourceUrl(`${path}`);
}
- 其次是在获取iframe 可以通过 @ViewChild来获取
@ViewChild('iframe') iframeElement:ElementRef<HTMLIFrameElement>; 来进行获取
- 通过监听iframe load事件来判断接受事件是否注册不能使用了 ,就需要在iframe内部传来一条信息来通知事件是否注册完成
iframeElement.addEventListener("load", () => {
iframeElement.contentWindow.postMessage("这是一条信息", "*");
});
window.parent.postMessage(true); //通知app事件注册成功
//接受iframe来的通知 基于rxjs去写事件的监听
fromEvent<MessageEvent>(window, 'message').pipe(map(data => data.data))
.pipe(takeUntil(this.ngUnsubscribe$))
.subscribe(isLoaded => {
if (isLoaded) {
this.templatePreviewIframe.nativeElement.contentWindow.postMessage(数据);
}
});
总结
html
<iframe
#iframe
[src]="src"
frameborder="0"
></iframe>
src:string;
ngUnsubscribe$= new Subject();
@ViewChild('iframe') iframeElement: ElementRef<HTMLIFrameElement>;
constructor(
private sanitizer: DomSanitizer
) {
this.src = this.sanitizer.bypassSecurityTrustResourceUrl(`${path}`);
}
ngOnInit(){
fromEvent<MessageEvent>(window, 'message').pipe(map(data => data.data))
.pipe(takeUntil(this.ngUnsubscribe$))
.subscribe(isLoaded => {
if (isLoaded) {
this.templatePreviewIframe.nativeElement.contentWindow.postMessage(数据);
}
});
}
ngOnDestroy(){
this.ngUnsubscribe$.next();
this.ngUnsubscribe$.complete();
}
iframe内容网页
ngOnInit(){
fromEvent(window, 'message').subscribe((event: MessageEvent<any>) => {
//todo
});
window.parent.postMessage(true); //通知app事件注册成功
}