uni-app 也能使用 App.vue?解决 uni-app 无法使用公共组件问题
借助 Vite 模拟出虚拟根组件(支持 SFC 的 App.vue),解决 uni-app 无法使用公共组件问题。
- 自定义虚拟根组件文件命名(
App.ku.vue文件命名支持更换) - 更高灵活度地获取虚拟根组件实例(获取
KuRootView的 Ref) - 自动提取
PageMeta到页面顶层(自动提升小程序PageMeta组件,用于阻止滚动穿透)
📦 安装
bash
pnpm add -D @uni-ku/root🚀 使用
1. 引入 @uni-ku/root
ts
// vite.config.(js|ts)
import Uni from "@uni-helper/plugin-uni"
import UniKuRoot from "@uni-ku/root"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
// 若存在改变 pages.json 的插件,请将 UniKuRoot 放置其后
UniKuRoot(),
Uni()
]
})Note
CLI:直接编写根目录下的
vite.config.(js|ts)HBuilderX:在根目录下创建
vite.config.(js|ts)并写入
2. 创建 App.ku.vue
(可自定义此根组件名称,请下拉至功能参考设置)
通过标签 <KuRootView /> 或 <ku-root-view /> 指定视图存放位置,并且可以将该标签放置到 template 内任意位置,但仅可有一个。
vue
<!-- src/App.ku.vue | App.ku.vue -->
<script setup lang="ts">
import { ref } from "vue"
const helloKuRoot = ref("Hello AppKuVue")
</script>
<template>
<div>{{ helloKuRoot }}</div>
<!-- 顶级 KuRootView -->
<KuRootView />
<!-- 或内部 KuRootView,无论放置哪一个层级都被允许,但仅可有一个! -->
<div>
<KuRootView />
</div>
</template>Note
CLI:需要在
src目录下创建App.ku.vue(或自定义名称)HBuilderX:直接在根目录下创建
App.ku.vue(或自定义名称)
Important
该标签与 Vue Router 中的
RouterView功能类似,但请注意,由于 uni-app Vue 的局限性,该功能并不完全等同于 Vue Router 的RouterView
功能
功能一:自定义虚拟根组件名称(默认:App.ku.vue)
1. 通过 rootFileName 自定义虚拟根组件名称
ts
// vite.config.(js|ts)
import Uni from "@uni-helper/plugin-uni"
import UniKuRoot from "@uni-ku/root"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
UniKuRoot({
// 默认含后缀 .vue,直接设置命名即可
rootFileName: "KuRoot"
})
// ...other plugins
]
})2. 创建 / 修改虚拟根组件为 KuRoot.vue
txt
// 将 App.ku.vue 重命名为 KuRoot.vue功能二:使用虚拟根组件实例(即:App.ku.vue)
有两种启用方式:局部启用 或 全局启用
一、局部启用
1. 暴露 App.ku.vue 中需要被使用的变量或方法
vue
<!-- src/App.ku.vue | App.ku.vue -->
<script setup lang="ts">
import { ref } from "vue"
const helloKuRoot = ref("Hello AppKuVue")
const exposeRef = ref("this is from App.ku.vue")
defineExpose({
exposeRef
})
</script>
<template>
<div>
<div>{{ helloKuRoot }}</div>
<KuRootView />
</div>
</template>2. 在 template 内编写 root="uniKuRoot"
并通过 const uniKuRoot = ref() 获取模板引用。
uniKuRoot只是一个变量名,你可以根据习惯自行命名。
vue
<!-- src/pages/*.vue -->
<script setup lang="ts">
import { ref } from "vue"
const uniKuRoot = ref()
</script>
<template root="uniKuRoot">
<view> Hello UniKuRoot </view>
</template>二、全局启用
1. 通过配置 enabledGlobalRef 开启全局自动注入 App.ku 实例
ts
// vite.config.(js|ts)
import Uni from "@uni-helper/plugin-uni"
import UniKuRoot from "@uni-ku/root"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
UniKuRoot({
enabledGlobalRef: true
}),
Uni()
]
})2. 暴露 App.ku.vue 中需要被使用的变量或方法
vue
<!-- src/App.ku.vue | App.ku.vue -->
<script setup lang="ts">
import { ref } from "vue"
const helloKuRoot = ref("Hello UniKuRoot")
const exposeRef = ref("this is from App.ku.vue")
defineExpose({
exposeRef
})
</script>
<template>
<div>
<div>{{ helloKuRoot }}</div>
<KuRootView />
</div>
</template>3. 通过 uni-app 内置方法 getCurrentPages() 获取暴露的数据
vue
<!-- src/pages/*.vue -->
<script setup lang="ts">
import { onMounted, ref } from "vue"
const pagesStack = getCurrentPages()
const uniKuRoot = ref()
onMounted(() => {
uniKuRoot.value = pagesStack[pagesStack.length - 1].$vm.$refs.uniKuRoot
})
</script>
<template>
<view> Hello UniKuRoot </view>
</template>功能三:过滤掉不需要根组件的页面
如果遇到一些不需要根组件的页面,可以设置 excludePages 选项进行过滤。
excludePages支持 glob 模式。
ts
// vite.config.(js|ts)
import Uni from "@uni-helper/plugin-uni"
import UniKuRoot from "@uni-ku/root"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
UniKuRoot({
excludePages: ["pages/exclude.vue", "pages/exclude/**/*.vue"]
}),
Uni()
]
})✨ 示例
以下示例均以 CLI 创建项目为例。
HBuilderX 项目配置同理,只需注意是否需要包含
src目录。
不仅是 Toast 组件,还可以是 Message、LoginPopup 等等。
1. 编写 Toast 组件
vue
<!-- src/components/GlobalToast.vue -->
<script setup lang="ts">
import { useToast } from "@/composables/useToast"
const { globalToastState, hideToast } = useToast()
</script>
<template>
<div v-if="globalToastState" class="toast-wrapper" @click="hideToast">
<div class="toast-box">welcome to use @uni-ku/root</div>
</div>
</template>
<style scoped>
.toast-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.toast-box {
background: white;
color: black;
}
</style>2. 实现 Toast 组合式 API
ts
// src/composables/useToast.ts
import { ref } from "vue"
const globalToastState = ref(false)
export function useToast() {
function showToast() {
globalToastState.value = true
}
function hideToast() {
globalToastState.value = false
}
return {
globalToastState,
showToast,
hideToast
}
}3. 挂载至 App.ku.vue
vue
<!-- src/App.ku.vue -->
<script setup lang="ts">
import GlobalToast from "@/components/GlobalToast.vue"
</script>
<template>
<KuRootView />
<GlobalToast />
</template>4. 在页面内部触发全局 Toast 组件
vue
<!-- src/pages/*.vue -->
<script setup lang="ts">
import { useToast } from "@/composables/useToast"
const { showToast } = useToast()
</script>
<template>
<view> Hello UniKuRoot </view>
<button @click="showToast">视图内触发展示 Toast</button>
</template>与 uni-helper-layouts 的区别
root 的核心理念是尽可能贴近 Vue 中的 App.vue,而 layouts 更偏向于类似 Nuxt 的布局系统。
root位于layouts之上,提供更高自由度root能够实现layouts的效果,并且更容易控制布局组件root支持PageMeta,并自动提取到页面顶层节点root提供多种方式使用模板引用