被多个 UView 系 UI 库 “抄” 的 @uni-ku/root 究竟怎么实现?

@uni-ku/root 解决的是 UniApp Vue3 里一个真实架构缺口:应用入口 App.vue 不能写视图,全局 Toast、登录弹窗、ConfigProvider 没有稳定的挂载点。

@uni-ku/root 的做法足够清晰、简单:用 Vite 插件,在编译期模拟虚拟根组件,开箱即用。

也是被多家 UView 系 UI 库 "抄" 的 同一套 实现虚拟根组件的插件。

"抄",我加了限定。因为 MIT 允许你做相近能力,同款思路到处开花,这没毛病。真正让我在意的,是另一件事:@uni-ku/root 被不断重复实现,改进 issue 却很少收到。

我的方案被验证,说明方向对。但如果社区只能不断堆叠相同功能的插件,生态只会停歇不前。

这篇讲清楚四件事:

  1. 告诉你 @uni-ku/root 到底解决什么痛点
  2. 把「同款实现」拆成能看懂的三刀
  3. 怎么 30 秒用上 正版@uni-ku/root
  4. 实际的 能力,下一站在 Oiyo(从插件模拟到框架原生)

01 问题:根部视图缺席

UniApp 的 App.vue 更像生命周期与全局样式入口,不是 原生 Vue 里那个能组织骨架的根组件。

结果是业务侧只能反复用三种方法凑合:

  1. 每个页面复制一份全局挂载
  2. 组件硬塞一层壳,重复运用
  3. 各种 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>

高级项(excludePagesenabledGlobalRef、自定义根文件名)以仓库 README 为准,比我这里更加全面。

这一节的结论只有一句:

老的 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 怎么选择适合场景

  1. 现有项目,只想挂全局 Toast / Provider → 继续 @uni-ku/root
  2. 新项目,或要布局 + 根上下文 + 约定化工程 → 直接 Oiyo,不必再拼 root 插件
  3. 长期 → 「靠插件模拟的组件」只是补丁期正解,Oiyo 才是最终的归宿

06 行动:双入口

入口 A:@uni-ku/root

如果 @uni-ku/root 帮到你,去 GitHub star 一下,好项目需要推广。

入口 B:Oiyo

Oiyo 是我全新的项目,是一个颠覆以往认知的 UniApp 增强型工程框架。开箱即用、支持用于商业项目!

在 UniApp 框架中最便捷、最符合直觉、开发体验、契合 AI 最好之一


如果觉得有帮助,记得 点赞 + 收藏 ~

踩坑请开 issue,反馈比 复制重新实现 更有价值。

下一站,不必人人再造相同插件。

相关推荐
鱼毓屿御1 小时前
从「只会聊」到「边想边做」的跃迁
前端·学习·react.js
sugar__salt2 小时前
Vue3 表单双向绑定与响应式核心 API 技术详解
前端·javascript·vue.js
郝亚军2 小时前
使用Vue 3和Nginx打包和部署Vue.js项目的一般步骤
前端·vue.js·nginx
SmartBoyW2 小时前
CSS弹性布局(Flexbox)学习笔记:从零开始搞懂弹性布局
前端·javascript
用户69371750013842 小时前
从代码生产者到 AI 协作者:软件工程师的角色重构
android·前端·后端
嘟嘟07172 小时前
搞懂 useState 从原生 DOM 到 React 惰性初始化的完整链路
前端·react.js·编程语言
牧艺2 小时前
cos-design RippleWater & SmokeFog:水面涟漪与烟雾雾气怎么做
前端·canvas·视觉设计
用户938515635073 小时前
写了这么久 useState,你真的知道它在干什么吗?
前端·javascript
慢功夫3 小时前
💡第七篇:VSCode语言服务中,代码跳转和诊断是怎么做的?
前端·visual studio code