最稳妥的水印方案是用css伪元素::before或::after,需设父容器position: relative,文字用transform: rotate(-30deg)倾斜并以color: rgba(0,0,0,0.08)控透明度,字体用rem或vw适配,铺满推荐伪元素而非background-image,挂载于#app等公共容器防滚动错位,全局固定需position: fixed + z-index: -1且html/body高100%,必加pointer-events: none,动态内容用css变量+js注入,ssr需useeffect/mounted补全,打印需@media print显式显示并调整颜色。

水印用 CSS 伪元素最稳妥
直接用 ::before 或 ::after 叠在页面内容上,不污染 DOM,也避免遮挡可交互元素。但得注意父容器要设 position: relative,否则伪元素会相对于 body 定位,跑偏。
- 水印文字必须转成
transform: rotate(-30deg),纯 CSS 实现倾斜,别用图片——加载慢、缩放糊、不能选中 - 透明度靠
color: rgba(0,0,0,0.08)控制,别用opacity,否则整个伪元素(含 transform)都变淡,边缘发虚 - 字体大小建议用
rem或vw,比如font-size: 6vw,适配不同屏幕;固定px在高分屏上会小得看不见 - 重复铺满:用
background-image: linear-gradient(...)也能做,但兼容性差(老 IE 不支持),且文字边缘锯齿明显
body 上加水印要防滚动错位
如果水印挂 body,用户一滚动,水印就“飘”在内容上面不动,看着像 bug。真实场景里,多数人想要的是「随内容滚动的背景水印」,不是「固定在视口的浮层」。
- 解决办法是把水印伪元素加到最大公共容器上,比如
#app或.content,而不是body - 如果非得全局加,就用
position: fixed+z-index: -1,但必须确认html和body高度为100%,否则水印只盖住首屏 -
pointer-events: none必加,不然水印区域点不到底下的按钮或输入框
动态水印(含用户信息)得用 JS 注入
静态 CSS 水印写死文字,没法塞用户名、时间戳或权限标识。这时候必须用 JS 创建伪元素或插入 div,再用内联样式控制。
- 推荐用
document.documentElement.style.setProperty('--watermark-text', 'admin@2024'),CSS 里用content: var(--watermark-text),比直接拼 HTML 安全 - 别用
innerHTML插水印div,XSS 风险高;尤其当用户名来自后端,必须textContent赋值 - 水印内容更新时,不要反复删 DOM 元素,改
style或 CSS 变量更轻量 - 注意 SSR 场景:服务端渲染时 JS 还没执行,水印为空,得在
useEffect(React)或mounted(Vue)里补
打印时水印消失?检查 @media print
很多页面一打印,水印就没了——不是代码失效,是浏览器默认禁用背景图和伪元素。得显式放开。
立即学习“前端免费学习笔记(深入)”;
- 必须加
@media print { .watermark::before { display: block !important; } },否则 Chrome/Firefox 默认隐藏 -
background-image方案在打印时更不可靠,部分打印机驱动直接丢弃背景 - 如果水印用了
rgba透明色,打印预览里可能显示为纯黑,建议打印专用样式里改用color: #ccc - 测试务必真机打印,PDF 导出预览 ≠ 实际打印效果
水印最难的不是加上去,是让它「存在感刚好」:太淡看不清,太浓挡操作;既不能被截图轻易抹掉,又不能干扰正常浏览。这些细节全藏在 position、z-index、pointer-events 和媒体查询的组合里。











