uni-app 小程序支持 teleport 了

前言

在 Vue 里写弹窗、抽屉、Toast、ActionSheet 这类组件时,teleport 是非常常见的能力。它可以把内容渲染到当前组件层级之外,避免被父级的 overflowz-index 或样式隔离影响。

但小程序不是浏览器,没有真正意义上的 document.body,也不能像 Web 一样随意移动 DOM 节点。所以以前做跨端组件时,经常需要这样写:

html 复制代码
<!-- #ifdef H5 -->
<teleport to="body">
  <popup />
</teleport>
<!-- #endif -->

<!-- #ifdef MP-WEIXIN -->
<root-portal>
  <popup />
</root-portal>
<!-- #endif -->

5.14 开始,小程序端支持直接写 teleport 了。开发者不再需要为了 H5 和小程序写两套条件编译,统一使用 Vue 的写法即可。

html 复制代码
<teleport to="body">
  <popup />
</teleport>

在 H5 端,它还是 Vue 原生的 teleport;在支持的小程序端,uni-app 会自动把它转换成平台原生的 root-portal

支持平台

目前支持的平台有:

  • 微信小程序
  • 支付宝小程序
  • 京东小程序

使用方式

用法和 Vue 保持一致:

html 复制代码
<template>
  <view class="page">
    <button @click="show = true">打开弹窗</button>

    <teleport to="body">
      <view v-if="show" class="mask" @click="show = false">
        <view class="dialog" @click.stop>
          我是弹窗内容
        </view>
      </view>
    </teleport>
  </view>
</template>

<script setup>
import { ref } from 'vue'

const show = ref(false)
</script>

编译到支持的小程序平台后,模板会变成类似这样:

html 复制代码
<root-portal>
  <view class="mask">
    <view class="dialog">
      我是弹窗内容
    </view>
  </view>
</root-portal>

也就是说,开发者写的是 teleport,小程序真正运行的是 root-portal

实现原理

这次支持本质上是一个编译期转换。

uni-app 在小程序编译阶段会识别模板里的 teleport 标签,并把它转换成 root-portal。这样既保留了 Vue 的开发体验,又能利用小程序平台已有的原生能力。

整体可以理解为:

html 复制代码
<teleport to="body">
  <view />
</teleport>

编译为:

html 复制代码
<root-portal>
  <view />
</root-portal>

需要注意的是,小程序的 root-portal 和 Vue 的 teleport 并不是完全等价的能力。Vue 的 teleport 可以通过 to 指定目标节点,而小程序的 root-portal 是把内容提升到页面根节点下渲染,并不支持传送到任意位置。

所以 uni-app 做的是能力映射,而不是在小程序里完整模拟 Web Teleport。

属性差异

虽然用法统一成了 teleport,但小程序端最终还是会落到 root-portal,所以部分属性会有差异。

to 和 defer

todefer 都是 Vue Teleport 的属性,但小程序 root-portal 没有对应能力,所以编译到小程序端时会被移除。

html 复制代码
<teleport to="body" defer>
  <popup />
</teleport>

会被转换成类似:

html 复制代码
<root-portal>
  <popup />
</root-portal>

如果代码同时跑 H5 和小程序,可以继续保留这些属性:to="body"defer 在 H5 端仍然有意义,只是在小程序端不会生效。

disabled

Vue Teleport 使用 disabled 控制是否禁用传送:

html 复制代码
<teleport :disabled="disabled">
  <popup />
</teleport>

小程序 root-portal 使用的是 enable,语义正好相反。因此编译到小程序时,uni-app 会自动做一次取反转换。

例如:

html 复制代码
<teleport :disabled="disabled">
  <popup />
</teleport>

会被处理成类似:

html 复制代码
<root-portal :enable="!disabled">
  <popup />
</root-portal>

开发者仍然按照 Vue 的 disabled 语义写即可,不需要手动改成 enable

相关推荐
Python私教3 分钟前
前端转 AI 全栈:别只做聊天框,SSE 与审批流才是分水岭
前端·人工智能
漂移的电子7 分钟前
解决Vue3 + TypeScript + Vite搭建的项目,动态路由页面加载失败的最隐蔽原因
javascript·ubuntu·typescript
前鼻音太阳熊21 分钟前
【MES系统】- 工业HMI状态机可视化实践:从手写SVG到Cytoscape.js的技术选型与踩坑
开发语言·javascript·ecmascript
晓得迷路了1 小时前
栗子前端技术周刊第 139 期 - Nuxt 4.5、Vue 3.6 RC、Angular 发布节奏...
前端·javascript·vue.js
鱼樱前端1 小时前
AI 会不会取代前端?我看了 2026 年 7 月整个市场,给你一个不吓人的答案
前端·程序员·ai编程
এ慕ོ冬℘゜1 小时前
原生 select 下拉框搜索失效踩坑:文本搜索与 ID 匹配不对应问题排查
前端·javascript·html
程序员爱钓鱼1 小时前
Rust Result 详解:可靠的错误处理机制
前端·后端·rust
鱼樱前端1 小时前
别再"学工具"了,先搭你的 AI 工作流
前端·ai编程·前端工程化
凤山老林3 小时前
从美团全栈化看 AI 冲击:前端转全栈,是自救还是必然
前端·人工智能·状态模式
777VG10 小时前
PostgreSQL +martin将多张表输出成一个 MVT
前端·数据库·postgresql