R后悔药 Rue.js
  • 首页
    • 深度指南
    • 实战例子
    • 快速上手
    • 路由指南
    • 术语表
    • 错误代码参考
  • API
    • 合作伙伴
    • 插件
    • 组件库
    • 工具链
    • Text.js
    • 新闻简报
    • 常见问题
    • 团队
    • 版本发布
    • 社区指南
    • 行为规范
    • 隐私政策
  • 赞助
  • 合作伙伴
设置
目录统计
总条目95
  • 你好,世界
  • 处理输入
  • Attribute 绑定
  • 条件与循环
  • Compiled 顶层控制流
  • 表单绑定
  • 简单组件
  • useApp().component 注册
  • Reactive Props Destructure
  • Props 与 useSetup
  • Slots 插槽
  • 原生 Web Components
  • Web Components 分项测试
  • 基础元素与自闭合标签
  • 表达式与插值
  • 属性、className、style 与 Props
  • 对象展开属性(spread props)
  • 条件渲染(?:、&&、null)
  • v-if / r-if 指令
  • v-show / r-show 指令
  • v-pre / r-pre 指令
  • v-once / r-once 指令
  • v-memo / r-memo 指令
  • v-text / r-text 指令
  • v-html / r-html 指令
  • v-on / r-on 指令
  • v-model / r-model 指令
  • 列表渲染与 key
  • v-for / r-for 指令
  • Template 无包装容器
  • Scoped Style 组件作用域样式
  • Fragments(<> … </>)
  • children 插槽与嵌套
  • 组件与 Props 传递
  • 动态组件(Component)
  • Suspense 异步边界
  • 异步组件懒水合
  • 事件处理
  • 受控输入
  • Refs 基础
  • Markdown 编辑器
  • 获取数据
  • 服务端预取
  • 资源
  • 资源(纯 JSX)
  • Context
  • 语言切换(_ 模型)
  • 路由 Demo:总览(嵌套路由)
  • 路由 Demo:守卫(beforeEnter)
  • 路由 Demo:实验页(受守卫保护)
  • 基础计数器
  • shallowRef 浅层 ref
  • triggerRef 手动触发
  • onActivated 缓存生命周期
  • Signal 路径读写
  • 独立 Signal 与派生值
  • isRef 判定示例
  • 无代理状态模型
  • 只读派生值
  • nextTick 真实业务场景
  • onScopeDispose 作用域清理
  • 渲染函数计数器
  • onRenderTriggered 调试
  • onDeactivated
  • onErrorCaptured 错误捕获
  • useState 计数器
  • useState 与局部快照
  • 条件渲染
  • map 列表渲染
  • JSX 数组子节点
  • 组件插槽中的 JSX 数组
  • Todo 应用
  • 基础待办事项
  • 本地待办事项
  • 用户资料编辑
  • useState 数组
  • useState 对象
  • 本地 ref 计数器
  • 排序、筛选与网格
  • Store Query Sync 与 URL 状态
  • 树状视图
  • SVG 图表
  • SVG 共享标签命名空间
  • 带过渡动效的模态框
  • Transition mode
  • 子调父方法
  • 父控子计数
  • 组件级 v-model
  • 命名 v-model
  • 组件 emit
  • Hello children
  • 基础 children Box
  • 嵌套 children Box
  • Layout children
  • 过渡动效

动态组件(Component)

1. 直接切换原生标签和组件定义

这里显式导入并使用 <Component>,is 可以在字符串标签名和函数组件之间来回切换。

children 已透传
当前 is: article
验证点:native element、直接组件定义、children 透传。
说明:切到 SalesCard 时,tone 也会跟着透传给目标组件。

2. 编译期有限组件注册表

下面这个小区域为 <Component> 显式提供有限 registry,编译器可以提前确定所有 可能的组件工厂。

运行时注册
CardView
metric 键在有限 registry 中解析到 RegisteredMetric
children 一样会透传
registry 直接写在 <Component> 上,不依赖全局字符串注册或运行时工厂查找。
编译器会把 registry 降为有限分支,再决定最终渲染哪个组件。
这也顺带验证了字符串名路径下的 children 透传。
注意:如果 is 最终解析成原生 input、select、textarea 之类的节点,写在 <Component> 上的 v-model / r-model 不能像静态原生标签那样在编译期展开,应该在最终原生节点处处理。
import {
  Component,
  type FC,
  ref,
} from '@rue-js/rue';

const SalesCard: FC<{ title: string; value: string; detail: string }> = (props) => (
  <article className="rounded-box border border-primary/25 bg-primary/10 p-4">
    <div className="text-xs uppercase tracking-[0.22em] opacity-60">{props.title}</div>
    <div className="mt-2 text-3xl font-semibold">{props.value}</div>
    <div className="mt-1 text-sm opacity-75">{props.detail}</div>
    <div className="mt-4">{props.children}</div>
  </article>
);

const StatusStrip: FC<{ title: string; detail: string }> = (props) => (
  <section className="rounded-box border border-info/25 bg-info/10 p-4">
    <div className="font-semibold">{props.title}</div>
    <div className="text-sm opacity-75">{props.detail}</div>
    <div className="mt-4">{props.children}</div>
  </section>
);

const RegisteredMetric: FC<{ title: string; value?: string; detail: string }> = (props) => (
  <article className="rounded-box border border-secondary/25 bg-secondary/10 p-4">
    <div className="text-xs uppercase tracking-[0.22em] opacity-60">{props.title}</div>
    <div className="mt-2 text-2xl font-semibold">{props.value ?? 'Registered'}</div>
    <div className="mt-1 text-sm opacity-75">{props.detail}</div>
    <div className="mt-4">{props.children}</div>
  </article>
);

const RegisteredNotice: FC<{ title: string; detail: string }> = (props) => (
  <section className="rounded-box border border-warning/35 bg-warning/15 p-4">
    <div className="font-semibold">{props.title}</div>
    <div className="mt-1 text-sm opacity-75">{props.detail}</div>
    <div className="mt-4">{props.children}</div>
  </section>
);

const Demo: FC = () => {
  const shell = ref<'native' | 'card' | 'notice'>('native');
  const registryMode = ref<'metric' | 'notice'>('metric');

  const resolveProps = () =>
    shell.value === 'native'
      ? {
          className: 'rounded-box border border-base-300 bg-base-100 p-4 shadow-sm',
        }
      : shell.value === 'card'
        ? {
            title: '今日成交额',
            value: '¥ 128,400',
            detail: '比昨日 +8.2%',
          }
        : {
            title: '状态切换',
            detail: '当前目标由 is 决定',
          };

  return (
    <div className="grid gap-6">
      <Component
        is={shell.value}
        registry={{ native: NativeArticle, card: SalesCard, notice: StatusStrip }}
        {...resolveProps()}
      >
        <span className="badge badge-outline badge-sm">children 已透传</span>
      </Component>

      <Component
        is={registryMode.value}
        registry={{ metric: RegisteredMetric, notice: RegisteredNotice }}
        title="运行时注册"
        value="CardView"
        detail="通过字符串名解析到已注册组件"
      >
        <span className="badge badge-outline badge-sm">children 一样会透传</span>
      </Component>
    </div>
  );
};

export default Demo;
页内导航
  • 1. 直接切换原生标签和组件定义
  • 2. 编译期有限组件注册表
文档
  • 深度指南
  • 实战例子
  • 快速上手
  • 术语表
  • 错误码参照表
关于
  • 常见问题
  • 团队
  • 版本发布
  • 社区指南
生态
  • 插件
  • 组件库
  • 路由指南
  • Text.js
© 2024-2026 Xiangmin Liu
Rue.JS