
先问个问题:你在用 UniApp 开发时,有没有过这种时刻 —— 想在 App.vue 里搭个全局布局,想统一管理应用外壳,想控制页面怎么渲染进来。然后你兴冲冲地打开 App.vue,准备写 <template>。
结果官方文档一句话把你劝退:"App.vue 是应用入口文件,但不能编写视图元素,也就是没有 <template>。"
翻译过来就是:App.vue 不让你写界面。
开发者:“我Chovy,写 template 给我写好的啊!”.gif
这事儿有多憋屈?在普通 Vue 项目里,App.vue 就是根组件啊。全局外壳、布局入口、页面容器,那可是想怎么组织就怎么组织。
但在 UniApp 里?App.vue 只能管生命周期和全局样式。能管"启动",能管"皮肤",就是管不了"骨架"。
这不是小问题。这是架构级的限制。
介绍:你才是真正的英雄(难蹦)
直到 Oiyo 出现,把这个"不可能"变成了"家常便饭"。

看到这段代码,你可能觉得:"不就是多了俩组件?"
但重点是:Oiyo 让 App.vue 能写 <template> 了。
这意味着你终于能在根部直接控制应用结构。页面在哪渲染、布局怎么接住、全局组件放哪 —— 这些原本只能靠想象的东西,现在能明明白白写在 App.vue 里。
template:终于能管根部视图了
在 Oiyo 里,App.vue 的 <template> 可以组织应用根部视图结构。
最小写法很简单:

一个 OiyoPage 组件,代表当前页面。就这么简单。
如果项目需要布局系统,写法也很直观:

OiyoLayout 读取当前页面指定的布局配置,OiyoPage 把页面内容渲染进去。
这种关系终于能在代码里清晰表达了:页面在哪里渲染,布局在哪里接住。以前这件事只能靠"想象",现在它就写在 App.vue 里,看得见摸得着。
如果你有应用级共享的外壳 —— ConfigProvider、NavBar、TabBar 这些,也能放在这里统一管理:

但有个原则:App.vue 是"总装台",不是"舞台"。
单个页面的逻辑放页面里,某类页面的共享结构放布局里。别什么都往 App.vue 塞,不然又乱又难维护了。
rootContext:根部状态怎么共享?
template 解决的是"页面怎么进来",rootContext 解决的是"根部状态怎么出去"。
在 App.vue 里这样定义:

应用标题、主题状态、应用级计数、外壳方法——这些根部信息放这里很合适。
但别把所有业务状态都塞进去。rootContext 只放应用全局必要的共享状态,复杂业务状态还是交给 Pinia 处理。
关键看 defineRootContext 的 return。你 return 出去什么,页面、组件、以及其他应用文件就能通过 useRootContext() 读到什么。
在页面、组件中读取:

在组合式函数、工具文件中读取:

应用场景很多。主题色的切换、全局弹窗的调用、应用级的配置读取,都可以走这条路。发挥想象力!
怎么玩 oiyo?
通过脚手架快速安装:
pnpm create oiyo@latest或者前往最佳实践仓库:
GitHub:https://github.com/skiyee/oiyo Gitee:https://gitee.com/skiyee/oiyo
总结
UniApp 官方说 App.vue 不能写 <template> — 这是限制。
Oiyo 让 App.vue 能写 <template> — 这是突破。
但真正有价值的不是"能写"本身,而是它把 Vue 工程里原本缺失的根部视图补了回来。代码写得更清晰,架构想得更明白,开发体验也顺畅了。
你觉得呢?如果你也有这个痛点,欢迎评论区聊聊。
如果觉得讲得不错,也可以点个关注 ❤
oiyo 官网:https://oiyo.js.org/
夜雨聆风