uniapp使用@uni-ku/root插件实现全局组件

最近做的uniapp项目中,发现uniapp的App.vue并没有template作为根目录,按照uniapp官网的说法,app.vue并不是页面,而是用来监听应用的生命周期、做一些全局样式和一些全局的变量(globalData),所以你在这里写template是没有用的。

那么问题来了,没有根目录那就没办法写全局组件了,在uniapp这种项目中,就得每个页面都去引入组件,那这样的话就会有重复的代码一直复制粘贴了。为了解决这个问题,我找到了两种办法,一种是使用插件在每个组件中都导入一遍对应的全局组件,一种是通过插件虚拟出根组件来,然后将全局会用到的组件放进来,使之成为一个真正的全局组件。

现在着重讲一下第二种办法,我们将会用到@uni-ku/root。(背景,本项目是由cli创建)

首先进行插件依赖的安装

bash 复制代码
npm install @uni-ku/root -D

然后,修改 vite.config.js 的配置

javascript 复制代码
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import AutoImport from 'unplugin-auto-import/vite'
import UniKuRoot from '@uni-ku/root'

// https://vitejs.dev/config/
export default defineConfig({
  ...
  plugins: [
    UniKuRoot(),  // 用来"虚拟"出一个根组件,让 uni-app 也能像 Vue 一样使用 App.vue,也就是能够用来做一些全局的组件挂载使用等操作
    uni(),
    AutoImport({
      imports: ['vue', 'pinia'],
    }),
  ],
  ....
})

然后在 src 下创建App.ku.vue,内容如下:

html 复制代码
<template>
  <KuRootView />
  <!-- 以下会组件被全局插入 -->
  <!-- Toast轻提示,需要引入@/components/toast/useToast.js然后再使用showToast打开 -->
  <Toast />
</template>
<script setup>
import Toast from '@/components/toast/index.vue'
</script>

<KuRootView /> 类似vuerouter的routerview标签,但是在uniapp中这个并不一样,比如vuerouter里面有各种生命周期等等。

toast.vue代码如下

html 复制代码
<template>
  <view v-if="visible" class="toast-mask" :class="{ 'can-penetrate': maskCanPenetrate }">
    <view class="toast-box" @tap.stop>
      <!-- 图标 -->
      <view v-if="iconType" class="toast-icon-box">
        <svg v-if="iconType === 'success'" class="success-icon toast-icon"><use href="#SelectCircleFilled"></use></svg>
        <svg v-else-if="iconType === 'fail'" class="fail-icon toast-icon"><use href="#CloseCircleFilled"></use></svg>
      </view>
      <!-- 文案 -->
      <text class="toast-title">{{ title }}</text>
    </view>
  </view>
</template>

<script setup>
import { useToast } from './useToast'

const { visible, title, iconType, maskCanPenetrate, showToast, hideToast } = useToast()

/* 导出给外部调用 */
defineExpose({ showToast, hideToast })
</script>

<style lang="scss" scoped>
.toast-mask {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  z-index: 9999;
  overflow: hidden;
  .can-penetrate {
    pointer-events: none;
  }
}
.toast-box {
  background-color: #fff;
  box-shadow: 0 8rpx 32rpx 0 rgba(0, 0, 0, 0.1);
  border-radius: 1998rpx 1998rpx 1998rpx 1998rpx;
  text-align: center;
  position: absolute;
  padding: 24rpx 48rpx;
  top: 112rpx;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;

  .toast-icon-box {
    display: flex;
    align-items: center;
    .toast-icon {
      margin-right: 16rpx;
      width: 32rpx;
      height: 32rpx;
      &.success-icon {
        color: $G6;
      }
      &.fail-icon {
        color: $R6;
      }
    }
  }
}

.toast-title {
  font-size: 32rpx;
  line-height: 32rpx;
  color: $T10;
}
</style>

useToast.js

javascript 复制代码
const visible = ref(false) // toast开关
const title = ref('') // 内容
const iconType = ref('fail') // icon: success / fail / ''
const toastDuration = ref(2000) // 默认延迟2000ms关闭
const maskCanPenetrate = ref(true) // 是否可以点击穿透(有宽高100%的蒙层存在),默认可以
let timer = null

export const useToast = () => {
  // 打开toast
  const showToast = ({ type = 'success', text = '', duration = 2000, canPenetrate = true } = {}) => {
    if (timer) clearTimeout(timer)
    visible.value = true
    iconType.value = type
    title.value = text
    toastDuration.value = duration
    maskCanPenetrate.value = canPenetrate

    if (toastDuration.value > 0) {
      timer = setTimeout(() => {
        hideToast()
      }, toastDuration.value)
    }
  }
  // 关闭toast
  const hideToast = () => {
    visible.value = false
    console.log('hideToast')
    if (timer) clearTimeout(timer)
    timer = null
  }
  return {
    visible,
    title,
    iconType,
    toastDuration,
    maskCanPenetrate,
    showToast,
    hideToast,
  }
}

onUnmounted(() => clearTimeout(timer))

现在toast就变成了全局的,我们只需要在需要使用到toast的页面引入useToast.js,然后操作里面的方法就可以打开了

html 复制代码
<template>
  <button @click="showToast">打开toast</button>
</template>
<script setup >
import { useToast } from '@/components/toast/useToast.js'

const { showToast } = useToast()


</script>
<style lang="scss" scoped>
// ....
</style>

这样就解决了在uniapp开发过程中一些全局组件的重复引入的问题了,代码也美观一些,更容易理解。

相关推荐
TE-茶叶蛋2 分钟前
html5-qrcode扫码功能
前端·html·html5
2501_906467633 分钟前
HTML5结合Vue3实现百万文件分块上传的思路是什么?
前端·html·html5·vue上传解决方案·vue断点续传·vue分片上传下载·vue分块上传下载
San30.3 分钟前
现代前端工程化实战:从 Vite 到 React Router demo的构建之旅
前端·react.js·前端框架
kirinlau5 分钟前
vue3+vite+scss项目使用tailwindcss
前端·css·scss
阿贾克斯的黎明7 分钟前
现代前端的魔法标签:HTML5 语义化标签全解析
前端·html·html5
菠菜盼娣10 分钟前
vue3知识点
前端·vue.js
JIngJaneIL14 分钟前
基于java+ vue建筑材料管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
土豆125016 分钟前
终端自治时代的 AI 开发范式:Claude Code CLI 全方位实操指南
前端·人工智能·程序员
Gazer_S19 分钟前
【Vue Router 路由守卫(Navigation Guards)指南:概念、执行顺序、beforeResolve、异步路由组件】
前端·javascript·vue.js