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

相关推荐
东风破_2 小时前
LLM 为什么记不住你?无状态、messages 和上下文管理
javascript·人工智能
梨子同志2 小时前
qiankun
前端
梨子同志2 小时前
Nginx
前端
梨子同志2 小时前
NestJS
前端
梨子同志2 小时前
Next.js
前端
谭光志3 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
Hilaku3 小时前
前端架构师的克制:什么时候该坚决对新技术说不?
前端·javascript·程序员
陈随易3 小时前
前端项目部署只要30秒
前端·后端·程序员
本末倒置1833 小时前
从 Vue CLI 迁移到 Rsbuild 后,JSX 突然全挂了?一次排查记录
前端