@uni-ku/root 解决的是 UniApp Vue3 里一个真实架构缺口:应用入口 App.vue 不能写视图,全局 Toast、登录弹窗、ConfigProvider 没有稳定的挂载点。
@uni-ku/root 的做法足够清晰、简单:用 Vite 插件,在编译期模拟虚拟根组件,开箱即用。
也是被多家 UView 系 UI 库 "抄" 的 同一套 实现虚拟根组件的插件。
"抄",我加了限定。因为 MIT 允许你做相近能力,同款思路到处开花,这没毛病。真正让我在意的,是另一件事:@uni-ku/root 被不断重复实现,改进 issue 却很少收到。
我的方案被验证,说明方向对。但如果社区只能不断堆叠相同功能的插件,生态只会停歇不前。
这篇讲清楚四件事:
- 告诉你 @uni-ku/root 到底解决什么痛点
- 把「同款实现」拆成能看懂的三刀
- 怎么 30 秒用上 正版 的
@uni-ku/root - 实际的 根 能力,下一站在 Oiyo(从插件模拟到框架原生)
01 问题:根部视图缺席
UniApp 的 App.vue 更像生命周期与全局样式入口,不是 原生 Vue 里那个能组织骨架的根组件。
结果是业务侧只能反复用三种方法凑合:
- 每个页面复制一份全局挂载
- 组件硬塞一层壳,重复运用
- 各种 runtime 操作
能跑,但不可持续。
说白了,缺的是一层稳定的「根部视图」。组件库再漂亮,也补不齐这个缺口。
02 方案:@uni-ku/root 的定位
作为 @uni-ku/root 的作者,一句话总结它,那就是:在改变任何代码的前提下,用最小侵入方式补上虚拟根。
你将得到:
- ⚡️ 编译期注入,接入快,就是快!
- 📦
App.ku.vue+<KuRootView />,心智靠近 Vue 的 App.vue - 🎨 全局 Toast / Message / 主题 Provider,挂一次全项目用
- 🔥 CLI / HBuilderX 的 UniApp Vue3 都能上
- 🛠 可排除页面、可拿根实例 ref、自动提升 PageMeta
- 💪 被多家 UI 库「对齐」过的方案,方向已经被市场验过
- 🚀 存量最小补丁;新项目还可以直接上 Oiyo 原生根
Github仓库:github.com/uni-ku/root
下面把实现摊开,你就知道同款到底在 "抄" 啥。
03 实现:同款虚拟根,核心就三刀
关键先看原则:@uni-ku/root 没有去硬刚 UniApp 运行时哲学。它改的是 Vite 编译产物。
第一刀:在 main 注册全局组件
命中 main.ts 时注入:
ts
import GlobalKuRoot from './App.ku.vue'
app.component('global-ku-root', GlobalKuRoot)
把一个 vue 文件,注册成 全局组件(重点)。后面所有包裹都依赖这一步。
你从这里可以看到,它本质上是一个组件,那么组件的缺点也很明显,也就是每次都重新加载 ,并且渲染树位于页面之下。
第二刀:把 KuRootView 编译成 slot
你在 App.ku.vue 里写的是标记:
vue
<template>
<KuRootView />
<!-- 全局 Toast 等挂在这里 -->
</template>
编译期把 <KuRootView /> 替换成 <slot />。
于是页面内容可以嵌进模板的任意层级,跟组件一样的原理。
第三刀:给每个页面包一层组件
插件读取页面清单(含子包),改写页面 template,在外层包一层 <global-ku-root>。
人话版最终结构:
页面 = 虚拟根的默认插槽内容。
三刀齐活,业务侧就是你想要的:
vue
<!-- App.ku.vue -->
<template>
<KuRootView />
<GlobalToast />
</template>
vue
<!-- 改写前:pages/xxx.vue -->
<script setup>
const { showToast } = useToast()
</script>
<template>
<button @click="showToast">弹一下</button>
</template>
xml
<!-- 改写后:pages/xxx.vue -->
<script setup>
const { showToast } = useToast()
</script>
<template>
<GlobalKuRoot> <!-- 👈 就是我们前面注册的全局组件 -->
<button @click="showToast">弹一下</button>
</GlobalKuRoot>
</template>
就这么简单。
UView 系里那些「全局根 / 虚拟根 / 编译塞一层」的同款味道,骨架基本就是这三步。
实现不神秘,所以容易被复用,也正因为不神秘,更只用一个正版源头就行。
04 上手:30 秒接入到项目
4-1 安装
打开项目所在命令行进行安装
bash
pnpm add -D @uni-ku/root
# 或 yarn / npm 同理
4-2 写入 vite 配置
CLI 直接改根目录 vite.config,HBX 没有就自己建一个。
ts
// vite.config.ts
import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni'
import UniKuRoot from '@uni-ku/root'
export default defineConfig({
plugins: [
UniKuRoot(),
Uni(),
],
})
4-3 创建 App.ku.vue
- CLI:在
src/App.ku.vue创建 - HBuilderX:在项目根
App.ku.vue创建
vue
<!-- App.ku.vue -->
<script setup lang="ts">
// 挂你的全局组件、主题等
</script>
<template>
<KuRootView />
<!-- 例如 <GlobalToast /> -->
</template>
高级项(excludePages、enabledGlobalRef、自定义根文件名)以仓库 README 为准,比我这里更加全面。
- Github仓库:github.com/uni-ku/root
- Gitee仓库:gitee.com/skiyee/uni-...
这一节的结论只有一句:
老的 UniApp Vue3 项目,root 仍然是最小、最直接的补丁。
05 代际交接:正在被 Oiyo 的根部能力承接
装完 root,故事还没完!
@uni-ku/root 回答的是:
真正有价值的讨论,不只是「三刀怎么写」,还有「这条能力最终该长在哪」。
@uni-ku/root 是补丁期正解。它证明了:UniApp 需要根部视图,而且编译期模拟走得通。
我正在持续迭代一个新项目 Oiyo 中,有一个新的答案:把同一条能力增强做进了框架本体,将其改造成原生,解决掉 root 所存在的问题。
5-1 从插件模拟 → 框架原生
@uni-ku/root:App.ku.vue + <KuRootView />,插件在编译期帮你包一层。有效,也优雅,本质仍是模拟。
Oiyo:采用了能写 <template> 真正的 App.vue。
- 页面出口:
<OiyoPage /> - 布局入口:
<OiyoLayout /> - 渲染链路写在代码表面,不再靠插件黑箱替你想象
其中最大的变化是从 App.ku.vue 变成 原生 Vue 同构 App.vue 的支持
5-2 从「能挂」→「能管」
@uni-ku/root 最强的体感是:Toast / 弹窗 / Provider,挂一次全项目用。
oiyo 采用了于 @uni-ku/root 不同的架构方式
- 用
defineRootContext在 App.vue 一键定义全项目共享的变量、方法 - 通过
useRootContext在页面、组件、组合式函数、工具函数里读取 - 不是全局性组件,渲染树真正的顶级节点
5-2-1 变量与方法定义
vue
<!-- App.vue -->
<script setup lang="ts">
const { theme, toast, loading, dialog } = defineRootContext(...)
</script>
<template>
<WdConfigProvider :theme-vars="theme.vars" :theme="theme.mode">
<OiyoLayout>
<OiyoPage />
</OiyoLayout>
<WdDialog selector="global" />
<WdToast selector="global" />
</WdConfigProvider>
</template>
5-2-2 变量与方法使用
ts
// use-hook.ts | api-client.ts | pinia.ts | 页面.vue | 组件.vue 等
const { toast } = useRootContext()
toast.success({ msg: 'oiyo.js.org' })
@uni-ku/root 让你少粘贴复制 N 个页面的代码;而 Oiyo 让 根部视图 和 根上下文 成为工程默认能力,这才是决定长期心智成本。
5-3 一张表看懂代际
| 维度 | @uni-ku/root | Oiyo 根部能力 |
|---|---|---|
| 形态 | 独立 Vite 插件 | 框架本体能力 |
| 渲染 | 全局组件 | 真正页面树 |
| 根文件 | App.ku.vue |
App.vue |
| 页面出口 | <slot /> |
<OiyoPage /> |
| 布局 | 可自搭 / 可配合 layouts | <OiyoLayout /> |
| 根状态 | ref / 自管 composable | defineRootContext |
| 心智 | 能力补充 | 默认存在 |
| 适用 | 旧项目最小侵入 | 新项目 / 迁移 |
5-4 怎么选择适合场景
- 现有项目,只想挂全局 Toast / Provider → 继续
@uni-ku/root - 新项目,或要布局 + 根上下文 + 约定化工程 → 直接 Oiyo,不必再拼 root 插件
- 长期 → 「靠插件模拟的组件」只是补丁期正解,Oiyo 才是最终的归宿
06 行动:双入口
入口 A:@uni-ku/root
- GitHub:github.com/uni-ku/root
- Gitee:gitee.com/skiyee/uni-...
如果 @uni-ku/root 帮到你,去 GitHub star 一下,好项目需要推广。
入口 B:Oiyo
- 官网:oiyo.js.org/
- 创建:
pnpm create oiyo@latest - 纯净模板: github.com/skiyee/oiyo
- 标准模板:github.com/wot-ui/oiyo...
Oiyo 是我全新的项目,是一个颠覆以往认知的 UniApp 增强型工程框架。开箱即用、支持用于商业项目!
在 UniApp 框架中最便捷、最符合直觉、开发体验、契合 AI 最好之一
如果觉得有帮助,记得 点赞 + 收藏 ~
踩坑请开 issue,反馈比 复制重新实现 更有价值。
下一站,不必人人再造相同插件。