普通视图

发现新文章,点击刷新页面。
今天 — 2026年1月23日首页

UniApp+vue3开发微信小程序,全局注册组件-不需要每个page都写一遍的那种

作者 波波酱
2026年1月23日 01:31

全局注册组件-不需要每个page都写一遍的那种

  • 需求来源:项目开发进入尾声,临近上线了客户要求给系统加水印
  • 功能分析:微信小程序没有vue的路由机制,不能像vue一样在App.vue里注册一个全局组件。麻烦一点的方式就是每个page都写一遍,虽然是copy paste,但是重复没有意义的事儿我是一点都不想干。那就写个组件,在源码层面,编译前给每个page都加上这个水印组件。
  • 实现思路
    • 写一个打水印的组件
    • 组件全局注册
    • 写一个vite插件,实现修改源码的功能
    • vite.config中使用插件
    • over

Step1: 水印组件实现

// /components/watermark/watermark.vue
<template>
  <!-- 增加一层判断,确保有图片才渲染 -->
  <view
    v-if="mask"
    class="watermark-mask"
    :style="{ backgroundImage: `url(${mask})` }"
  ></view>
</template>

<script setup>
import { ref, onMounted, nextTick } from "vue";

import { useStore } from "vuex";
const store = useStore();
const user = computed(() => store.getters.user);

const mask = ref("");

const draw = () => {
  try {
    // 微信小程序创建离屏 canvas 的标准写法
    const canvas = uni.createOffscreenCanvas({
      type: "2d",
      width: 200,
      height: 200,
    });
    const ctx = canvas.getContext("2d");

    ctx.font = "14px Arial";
    ctx.fillStyle = "rgba(200, 200, 200, 0.2)";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";

    ctx.translate(100, 100);
    ctx.rotate(-Math.PI / 9);
    ctx.fillText(`${user.value.nickName || ''}    ${user.value.phonenumber || ''}`, 0, 0);

    // 生成 base64
    mask.value = canvas.toDataURL();
    console.log("水印生成成功");
  } catch (e) {
    console.error("水印生成失败:", e);
  }
};

watch(
  () => store.getters.user,
  () => {
    draw();
  },
  {
    deep: true,
  }
);

onMounted(() => {
  console.log("水印组件已挂载");
});
</script>

<style scoped>
.watermark-mask {
  position: fixed;
  inset: 0;
  z-index: 999999;
  pointer-events: none;
  background-repeat: repeat;
  /* 确保不遮挡背景 */
  background-color: transparent;
}
</style>

Step2: 组件全局注册

方案一:在pages.json中注册
// 在你的pages.json中加入这段代码全局注册一下
"easycom": {
    "autoscan": true,
    "custom": {
        "^watermark$": "@/components/watermark/watermark.vue"
    }
}

hh,于是问题出现了,编译好的代码里能看到watermark标签,但是小程序模拟器审查元素没有这个元素。😁😁

组件代码中已经引入了watermark组件,且标签类似 <watermark u-i="xxx" bind:__l="__l" />,ai说标签带有u-i(uni-app唯一标识)和bind:__l(生命周期绑定),说明uniapp编译器已经把它识别为一个组件了,但是模拟器里找不到该节点,说明页面.json中没有注册这个组件。微信小程序会直接忽略没有在json中注册的节点,所以模拟器里找不到。

插件是动态注入的,uniapp的依赖扫描器可能在插件运行前就完成了扫描,导致它没有把watermark写进页面的usingComponents里。方案一PASS!!

方案二:在main.js中注册

防止uniapp扫描依赖漏掉,直接全局强制注册组件

import watermark from "@/components/watermark/watermark.vue";
  
const app = createSSRApp(App);
app.component("watermark", watermark);

Step3: vite插件修改源码

import path from "path";
import fs from "fs";

export default function viteInsetLoader() {
  // 读取 pages.json
  // 项目里我的每个page里都有一些子组件./components/...vue,只需要给page级页面注册就好了,为了减少代码冗余我这里加了个是否是page的判断
  const pagesJsonPath = path.resolve(__dirname, "pages.json");
  const pagesJson = JSON.parse(fs.readFileSync(pagesJsonPath, "utf-8"));
  const pages = pagesJson.pages.map((item) => item.path);
  return {
    name: "vite-inset-loader",
    // 确保在 uni 插件之前执行,这样修改的是原始源码
    enforce: "pre",
    transform(code, id) {
      // 1. 过滤非页面文件
      const isPage = pages.find((item) => id.indexOf(item) > -1);
      if (!isPage || !id.endsWith(".vue")) return null;

      // 2. 检查代码中是否包含 <template>
      if (code.includes("<template>")) {
        console.log("--- 正在注入水印到页面 ---:", id);

        // 3. 注入逻辑:建议注入在第一个 <view> 之后,或者 </template> 之前
        // 尽量匹配带空格或换行的 </template>
        const newCode = code.replace(/([\s\S]*)<\/template>/, (match, p1) => {
          return `${p1}  <watermark />\n</template>`;
        });
        return {
          code: newCode,
          map: null,
        };
      }
      return null;
    },
  };
}

Step4: 使用插件

import uni from "@dcloudio/vite-plugin-uni";
import appendWatermark from "./vite-plugin-watermark";

export default defineConfig({
  plugins: [
    uni(),
    appendWatermark(), // 写在uni后面
  ]
})

补充一句:web-view的页面水印加不上,就算是z-index:99999999也不起作用,小程序的webview层级太高咧,h5自行加吧。

先总结到这里,持续更新。。。。

❌
❌