我能够使用来自 an article about Portals in React 的一些见解来创建一个 Vue 组件,该组件能够在新窗口中挂载其子组件,同时保持反应性!很简单:
<window-portal>
I appear in a new window!
</window-portal>
试试这个codesandbox!
该组件的代码如下:
<template>
<div v-if="open">
<slot />
</div>
</template>
<script>
export default {
name: 'window-portal',
props: {
open: {
type: Boolean,
default: false,
}
},
data() {
return {
windowRef: null,
}
},
watch: {
open(newOpen) {
if(newOpen) {
this.openPortal();
} else {
this.closePortal();
}
}
},
methods: {
openPortal() {
this.windowRef = window.open("", "", "width=600,height=400,left=200,top=200");
this.windowRef.addEventListener('beforeunload', this.closePortal);
// magic!
this.windowRef.document.body.appendChild(this.$el);
},
closePortal() {
if(this.windowRef) {
this.windowRef.close();
this.windowRef = null;
this.$emit('close');
}
}
},
mounted() {
if(this.open) {
this.openPortal();
}
},
beforeDestroy() {
if (this.windowRef) {
this.closePortal();
}
}
}
</script>
关键是this.windowRef.document.body.appendChild(this.$el)这一行;这一行有效地从父窗口中删除了与 Vue 组件(*<div>)关联的 DOM 元素,并将其插入到子窗口的主体中。由于这个元素与 Vue 通常会更新的元素相同的引用,只是在不同的地方,一切正常工作 - Vue 继续更新元素以响应数据绑定的变化,尽管它被安装在一个新窗户。我真的很惊讶这是多么简单!