Fuz*_*yma 6 vue.js vuejs3 vue-teleport
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')
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue@3.0.0-rc.9/dist/vue.global.js"></script>
<div id="app"></div>
Run Code Online (Sandbox Code Playgroud)
如您所见,控制台甚至报告说需要已经渲染传送目标。但是我怎么能告诉 vue 先渲染哪个组件呢?在我的例子中,目标在传送前的 dom 中。
这在 portal-vue 中不是问题,而且非常令人失望,因为它使整个概念变得不那么可用。
Dan*_*iel 10
Vue 错误消息指向了问题所在。它说Invalid Teleport target on mount: null
问题是目标不存在YET。
这可以通过仅在安装组件后渲染传送部分来轻松解决。
它看起来像这样的东西,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')
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue@3.0.0-rc.9/dist/vue.global.js"></script>
<div id="app"></div>
Run Code Online (Sandbox Code Playgroud)
Bat*_*Man 10
或者可以创建MountedTeleport
组件来扩展 @Daniel 解决方案,以便模型不被污染
<template>
<Teleport :to="to" v-if="isMounted"><slot></slot></Teleport>
</template>
<script>
export default {
name: "MountedTeleport",
props: ['to'],
data() {
return {isMounted: false}
},
mounted() {
this.isMounted = true;
}
}
</script>
Run Code Online (Sandbox Code Playgroud)
它被用作
<MountedTeleport to="#given-selector">your html....</MountedTeleport>
Run Code Online (Sandbox Code Playgroud)
如果仍然传送初始化第一个可以使用
mounted() {
this.$nextTick(() => {
this.isMounted = true;
})
}
Run Code Online (Sandbox Code Playgroud)
或者即使先渲染然后可能setTimeout
会完成这项工作
模板引用是对真实DOM 元素的引用,因此在 DOM 实际渲染之前,模板引用将只是null
.
Vue采用虚拟dom架构。您的代码通常仅指示 vue 更改虚拟 dom,并将这些更改同步到真实 dom 的过程称为修补。修补是异步进行的。this.$refs
Vue 会在必要时(每次修补 dom 之后)负责填充/重新填充,并且最早$refs
可访问的时间通常是已安装的钩子(如果有条件渲染,则可能会更晚)。
不过,并不是说$refs
以后就永远不会改变。Vue 组件安装顺序有时也非常令人惊讶(例如,首先安装子组件还是父组件?)。通常所有这些都不会成为任何问题,因为在反应性世界中这些序列应该无关紧要。然而,当你开始做命令性的事情时——例如依靠生命周期钩子来设置一个标志,稍后驱动模板的条件渲染等——你会再次容易受到反应式框架首先试图解决的问题的影响。
因此,为了更安全的方法,您最好保持反应性,观察变化$refs
并做出反应。但问题是$refs 不是响应式的。
但 Vue 允许 ref 指令采用一个函数,以便您可以控制要存储模板引用的位置。因此,您可以将 ref 存储在本质上是反应性的东西中 - 即data
。将数据作为道具输入到子组件中,并将您的传送目标绑定到该道具。现在一切又恢复了反应。
为了演示,我修改了您的示例,添加了两个额外的按钮来控制是否渲染传送目标 div。现在您无法再访问已安装挂钩中的目标 div 模板引用,因为在您单击“传送”按钮之前,div 将不会被渲染:
const app = {
template: `
<div>
<h1>App</h1>
<button @click="show=true"> teleport on </button>
<button @click="show=false"> teleport off </button>
<div v-if="show" :ref="(el) => dest = el"></div>
<comp :target="dest"/>
</div>`,
data() {
return { dest: null, show: false };
}
}
Vue.createApp(app).component('comp', {
props: ["target"],
template: `
<div>
A component
<Teleport v-if="target !== null" :to="target">
Hello From Portal
</Teleport>
</div>`
}).mount('#app')
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="app"></div>
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1588 次 |
最近记录: |