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自行加吧。
先总结到这里,持续更新。。。。