问题描述
Vue 3具有一项新的Teleport功能,该功能取代了vue 2中的portal-vue插件。但是,我发现无法将组件移植到受vue控制的位置(在vue应用程序中)。仅当移植到外部(主体,其他元素...)时有效。
const app = {
template: `
<div>
<h1>App</h1>
<div id="dest"></div>
<comp />
</div>`
}
Vue.createApp(app).component('comp',{
template: `
<div>
A component
<Teleport to="#dest">
Hello From Portal
</Teleport>
</div>`
}).mount('#app')
<script src="https://unpkg.com/vue@3.0.0-rc.9/dist/vue.global.js"></script>
<div id="app"></div>
如您所见,控制台甚至报告说,一个传送目标已经被渲染。但是如何告诉vue首先渲染哪个组件?在我的示例中,目标位于传送前的dom中。
这在Portal-Vue中不是问题,而且非常可惜,因为它使整个概念的可用性降低。
解决方法
Vue错误消息指出了该问题。它说Invalid Teleport target on mount: null
问题是目标不存在还。
仅在安装组件后才渲染传送部分,即可轻松解决此问题。
似乎 就像这样,Vue应该在没有显式检查的情况下进行处理。当您将id作为字符串传递时,很难确定目标是否为Vue组件,尤其是尚未渲染时。但是我只是在推测团队的意图。
const app = {
template: `
<div>
<h1>App</h1>
<div id="dest"></div>
<comp />
</div>`
}
Vue.createApp(app).component('comp',{
template: `
<div>
A component
<Teleport to="#dest" v-if="isMounted">
Hello From Portal
</Teleport>
</div>`,data: function(){
return {
isMounted: false
}
},mounted(){
this.isMounted = true
}
}).mount('#app')
<script src="https://unpkg.com/vue@3.0.0-rc.9/dist/vue.global.js"></script>
<div id="app"></div>
,
我看到没有必要在Vue应用程序创建的DOM树中定位元素,因为传送的逻辑和内容与目标无关(我的意思是逻辑和内容都独立于风格) ),它不像scoped slot那样可能需要一些数据才能从子组件中获取给父组件。
示例1 :如我在传送之前依赖于目标样式,在下面的示例中,主体具有relative
位置,该位置可使我们的传送按预期显示(作为弹出窗口),但是如果我们将位置更改为fixed
,则传送会受到影响(请参见示例2 )
const app = Vue.createApp({});
app.component('modal-button',{
template: `
<button @click="modalOpen = true">
Open full screen modal! (With teleport!)
</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
`,data() {
return {
modalOpen: false
}
}
})
app.mount('#app')
.modal {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0,.5);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.modal div {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: white;
width: 300px;
height: 300px;
padding: 5px;
}
<script src="https://unpkg.com/vue@3.0.0-rc.9/dist/vue.global.js"></script>
<body style="position: relative;">
<div id="app" >
<h3>Tooltips with Vue 3 Teleport</h3>
<div>
<modal-button></modal-button>
</div>
</div>
</body>
示例2
const app = Vue.createApp({});
app.component('modal-button',.5);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.modal div {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: white;
width: 300px;
height: 300px;
padding: 5px;
}
<script src="https://unpkg.com/vue@3.0.0-rc.9/dist/vue.global.js"></script>
<body style="position: fixed;">
<div id="app" >
<h3>Tooltips with Vue 3 Teleport</h3>
<div>
<modal-button></modal-button>
</div>
</div>
</body>
,
在此问题上 GH 包含问题解决。 应该是这样的
//Root template in App.vue(it doesn't need to wrap in a div or smth else)
<template>
<div class="modal" :style="style">
<div class="modal-background"></div>
<div class="modal-content">
<div id="modal"></div>
</div>
<button class="modal-close is-large" aria-label="close"
@click="modal.hideModal"></button>
</div>
// App instance
<section class="section">
<div class="container">
<Navbar/>
<router-view/>
</div>
</section>
</template>