diff --git a/.env.development b/.env.development
index b199985..f498e9d 100644
--- a/.env.development
+++ b/.env.development
@@ -18,9 +18,9 @@ VITE_APP_TITLE=Azizi H5 (Development)
VITE_API_BASE_URL=https://api.likeichat.com
# 正式用户 8832199
-VITE_USER_AUTH=8C8D1E758D7B032FFB5664C4FE41C91C.djIlM0ExOTkwNjg1NTY1MzMzNTkwMDE3JTNBTElLRUklM0ExNzc5MjUxMDM2MjEyJTNBMTc3NjY1OTAzNjIxMg==
+VITE_USER_AUTH=69C0D45E86F7D7BFA520B39EC75670F7.djIlM0ExOTkwNjg1NTY1MzMzNTkwMDE3JTNBTElLRUklM0ExNzgxMzM0Mjc1ODM3JTNBMTc3ODc0MjI3NTgzNw==
# 正式用户 8826796
-# VITE_USER_AUTH=23116F749A21862A2B7217CDEE0FBF45.djIlM0ExOTYzNzkwMDM3NzQwNDY2MTc4JTNBTElLRUklM0ExNzc4OTAxNTg4MDA5JTNBMTc3NjMwOTU4ODAwOQ==
+# VITE_USER_AUTH=EB80A174E9F927A811009B05DE982B98.djIlM0EyMDA1ODU0NTE0ODQzMTYwNTc4JTNBTElLRUklM0ExNzgwOTA0NTk5Njg1JTNBMTc3ODMxMjU5OTY4NQ==
# 正式用户 10086
# VITE_USER_AUTH=E71AE3738A4FA44BD2E895CBE176AC81.djIlM0EyMDA1ODU0NTE0ODQzMTYwNTc4JTNBTElLRUklM0ExNzc4NjQwMzczOTU4JTNBMTc3NjA0ODM3Mzk1OA==
diff --git a/docs/安全/前端加固-中文.md b/docs/安全/前端加固-中文.md
index 22724f9..e0f3f87 100644
--- a/docs/安全/前端加固-中文.md
+++ b/docs/安全/前端加固-中文.md
@@ -4,7 +4,7 @@
这份文档用于记录当前主分支已经落地的前端防扒取加固方案。
-核心目标是提高扒图、抄页面、低成本复刻的门槛,而不是宣称“前端已经绝对防住”。
+核心目标是提高扒图、抄页面、低成本复刻的门槛,而不是宣称“前端已经绝对防住”。只要页面能在客户端完整渲染,前端就无法彻底阻止抓包、截图、Hook、像素提取或人工复刻。
## 范围
@@ -29,13 +29,13 @@
表现:
-- 公共页面仍可正常访问
-- 受保护页面在非 App 环境下会跳转到 `/#/not_app`
-- 受保护页面内的接口请求在缺少 App bridge 时会被拒绝
+- 公共页面仍可正常访问。
+- 受保护页面在非 App 环境下会跳转到 `/#/not_app`。
+- 受保护页面内的接口请求在缺少 App bridge 时会被拒绝。
### 2. 运行时防调试与防复制限制
-对受保护页面增加了一层轻量运行时限制。
+受保护页面增加了轻量运行时限制。
主要文件:
@@ -45,9 +45,9 @@
表现:
-- 屏蔽常见开发者工具快捷键
-- 屏蔽右键、拖拽、复制、文本选择等交互
-- 降低长按保存图片的便利性
+- 屏蔽常见开发者工具快捷键。
+- 屏蔽右键、拖拽、复制、文本选择等交互。
+- 降低长按保存图片的便利性。
### 3. `Activities` / `Ranking` 图片受保护地址层
@@ -59,164 +59,90 @@
- [src/utils/protectedAssets.js](/D:/programs/likei-h5/src/utils/protectedAssets.js)
- [src/utils/protectedAssetRuntime.js](/D:/programs/likei-h5/src/utils/protectedAssetRuntime.js)
- [src/directives/smartImage.js](/D:/programs/likei-h5/src/directives/smartImage.js)
+
+表现:
+
+- 模板中更多使用 `likei-protected:` 形式的受保护地址。
+- 运行时再解析为真实图片地址。
+- 大部分图片最终会走缓存并转换成 blob/object URL。
+
+### 4. 图片缓存和单图版本控制
+
+图片缓存用于减少重复网络下载,但不再追求过大容量。
+
+主要文件:
+
+- [src/config/imageCacheVersion.js](/D:/programs/likei-h5/src/config/imageCacheVersion.js)
- [src/utils/image/imageCacheManager.js](/D:/programs/likei-h5/src/utils/image/imageCacheManager.js)
-表现:
+当前策略:
-- `getPngUrl()` / `getWebpUrl()` 会先输出 `likei-protected:...`
-- 运行时只有在真正渲染时才解析成真实地址
-- 大部分图片最终会走缓存并转换成 blob/object URL
-- 即使个别页面漏了 `v-smart-img`,运行时也有兜底解析
+- 默认最多缓存 120 张图片。
+- 最大缓存体积为 80MB。
+- 普通发版不更新全局缓存版本。
+- 单张图片替换且不改名时,在 `assetVersions` 中给该图片加版本。
-### 4. `background-image` 泄露点补丁
+示例:
-部分重点页面之前通过内联 `background-image: url(...)` 直接暴露真实地址,这类点位已经做了兼容收口。
-
-已调整文件:
-
-- [src/views/Activities/LesserBairam/index.vue](/D:/programs/likei-h5/src/views/Activities/LesserBairam/index.vue)
-- [src/views/Activities/LuckyDollars/Season3/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season3/index.vue)
-- [src/views/Activities/SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue)
-- [src/views/Activities/SpringFestival/Task.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Task.vue)
-- [src/views/Ranking/KingAndQueen/TopList.vue](/D:/programs/likei-h5/src/views/Ranking/KingAndQueen/TopList.vue)
-
-表现:
-
-- 写入 CSS 前先通过受保护地址辅助方法解析
-- 模板和样式字符串里直接出现真实图链的情况更少
-
-### 5. 构建产物可读性降低
-
-构建输出文件名从可读页面名改成了哈希命名。
-
-主要文件:
-
-- [vite.config.js](/D:/programs/likei-h5/vite.config.js)
-
-表现:
-
-- JS chunk 文件名不再直接暴露 `TopList`、`WeeklyStar`、`Ranking` 等页面名
-- CSS 文件名也改为哈希命名
-- 静态资源文件名同样使用哈希
-
-### 6. 生产包自动清理日志,并保留可切换开关
-
-生产相关打包模式下,会自动清掉常见调试日志;同时保留一个方便排查 App 内问题的“保留日志”构建开关。
-
-主要文件:
-
-- [vite.config.js](/D:/programs/likei-h5/vite.config.js)
-- [package.json](/D:/programs/likei-h5/package.json)
-- [scripts/build-with-version.js](/D:/programs/likei-h5/scripts/build-with-version.js)
-
-表现:
-
-- `production` 下自动移除 `console.log`
-- `production` 下自动移除 `console.debug`
-- `production` 下自动移除 `console.info`
-- 生产相关模式下自动移除 `debugger`
-- `console.warn` 和 `console.error` 保留,方便线上排查
-- Jenkins 仍可继续使用原有默认打包命令
-
-常用命令:
-
-```bash
-npm run build:prod
-npm run build:prod:logs
+```js
+assetVersions: {
+ 'Activities/LuckyDollars/Season6/bg0.png': '20260514',
+}
```
-### 7. 部分高风险页面改为延迟挂载主内容 DOM
+### 5. 构建产物收口
-部分活动页和榜单页原来使用 `v-show="!isLoading"` 控制主内容,这会让完整 DOM 树在较早阶段就已经挂载出来,也容易和 `IntersectionObserver` 的注册时序冲突。
-
-现在这几页已经改成 `v-if="!isLoading"` 再挂载主内容:
-
-- [src/views/Activities/LoginReward/index.vue](/D:/programs/likei-h5/src/views/Activities/LoginReward/index.vue)
-- [src/views/Activities/LuckyDollars/Season4/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season4/index.vue)
-- [src/views/Activities/SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue)
-- [src/views/Ranking/Couple/index.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/index.vue)
-- [src/views/Ranking/Overall/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Overall/Ranking.vue)
-
-同步补了两类兜底:
-
-- 对依赖 `IntersectionObserver` 的页面,在 `await nextTick()` 之后再注册观察器
-- 预加载结束逻辑仍然放在 `finally` 中,避免预加载失败时页面长期空白
-
-### 8. 装饰背景堆图改为可选 canvas 渲染
-
-公共背景组件现在支持可选的 canvas 渲染模式,适合处理活动页和榜单页那种“纯装饰背景层层堆图”的场景。
-
-主要文件:
-
-- [src/components/BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue)
-
-当前已接入的高风险页面:
-
-- [src/views/Activities/LesserBairam/index.vue](/D:/programs/likei-h5/src/views/Activities/LesserBairam/index.vue)
-- [src/views/Activities/LoginReward/index.vue](/D:/programs/likei-h5/src/views/Activities/LoginReward/index.vue)
-- [src/views/Activities/LuckyDollars/Season3/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season3/index.vue)
-- [src/views/Ranking/Couple/index.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/index.vue)
-- [src/views/Activities/LuckyDollars/Season4/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season4/index.vue)
-- [src/views/Activities/SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue)
-- [src/views/Ranking/GamesKing/index.vue](/D:/programs/likei-h5/src/views/Ranking/GamesKing/index.vue)
-- [src/views/Ranking/KingAndQueen/TopList.vue](/D:/programs/likei-h5/src/views/Ranking/KingAndQueen/TopList.vue)
-- [src/views/Ranking/Overall/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Overall/Ranking.vue)
-- [src/views/Ranking/WeeklyStar/WeeklyStar.vue](/D:/programs/likei-h5/src/views/Ranking/WeeklyStar/WeeklyStar.vue)
-
-表现:
-
-- 多张纯装饰背景图会先合成到单个 canvas 中,而不是在 DOM 里直接挂一个个 `
`
-- 从 Elements 面板直接看页面背景结构会比之前更不直观
-- 受保护图片地址仍然继续走现有的运行时解析和缓存链路
-
-### 9. 生产构建混淆再收紧,并补一轮明显语义类名收口
-
-生产相关构建模式现在显式开启更严格的 `esbuild` 压缩参数,同时针对重点页面收了一轮最显眼的本地类名。
+生产构建使用内容 hash,减少可读文件名暴露,也提升缓存稳定性。
主要文件:
- [vite.config.js](/D:/programs/likei-h5/vite.config.js)
-- [src/views/Ranking/Couple/index.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/index.vue)
-- [src/views/Activities/LuckyDollars/Season4/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season4/index.vue)
-- [src/views/Activities/SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue)
-- [src/views/Activities/SpringFestival/Ranking.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Ranking.vue)
-- [src/views/Ranking/Overall/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Overall/Ranking.vue)
-- [src/views/Ranking/WeeklyStar/WeeklyStar.vue](/D:/programs/likei-h5/src/views/Ranking/WeeklyStar/WeeklyStar.vue)
+- [nginx/default.conf](/D:/programs/likei-h5/nginx/default.conf)
+- [nginx/default_hooka.conf](/D:/programs/likei-h5/nginx/default_hooka.conf)
-### 10. `Ranking/Couple` 注释修复与背景组件说明补充
+表现:
-近期对 [src/views/Ranking/Couple/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/Ranking.vue) 做了一次编码与注释清理:
+- JS/CSS/资源文件名使用内容 hash。
+- HTML 短缓存,静态资源长缓存。
+- 内容不变的资源在普通发版后可以继续命中缓存。
-- 清除了上一次编辑中遗留的乱码注释
-- 将核心业务注释统一收口为中文,便于后续维护
+### 6. CSS 背景受保护地址桥接
-同时补充说明两个容易混淆的背景层工具点:
+部分页面必须使用 `background-image`。这类场景通过 `toCssBackgroundImage()` 把 `likei-protected:` 地址转换为 CSS 可用的 `url(...)`。
-- [src/components/BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue) 的 `useCanvas` 是显式开关,不默认全局开启
-- [src/utils/protectedAssets.js](/D:/programs/likei-h5/src/utils/protectedAssets.js) 中的 `toCssBackgroundImage()` 用于把 `likei-protected:` 地址还原成 CSS 可用的 `background-image: url(...)`
+主要文件:
-## 这套方案能挡住什么
+- [src/utils/protectedAssets.js](/D:/programs/likei-h5/src/utils/protectedAssets.js)
-当前前端加固,对以下场景有一定抬门槛作用:
+## Canvas 背景方案已取消
-- 普通浏览器直接打开受保护页面
-- 低门槛 DOM 抓取
-- 从模板或页面源码里直接抄图片地址
-- 从可读的构建产物文件名里反推页面用途
-- 简单右键、拖拽、长按保存
-- 依赖“页面初始 DOM 直接可见”的低成本抄结构方式
+之前 [BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue) 增加过 `useCanvas` 模式,用于把多张装饰背景绘制到单个 canvas 中,减少在 Elements 面板里直接看到背景 `
` 列表的便利性。
+
+经过低端机性能排查后,该方案已取消。
+
+当前约定:
+
+- `BackgroundLayer` 仍保留 `useCanvas` 入参,兼容历史页面调用。
+- 当前实现不再生成 canvas,统一渲染为普通 `
`。
+- 图片仍继续走受保护路径解析和图片缓存链路。
+- 不再把 canvas 背景作为默认防扒取方案。
+
+取消原因:
+
+- 大尺寸 canvas 会增加内存占用和绘制成本。
+- 低端安卓 WebView 中,canvas 背景叠加弹幕、跑马灯、头像列表时容易放大卡顿。
+- canvas 只能降低从 Elements 面板直接复制背景结构的便利性,防不住抓包、运行时 Hook 或最终像素提取。
+- 页面打开速度、滑动和动画流畅度优先级高于这点有限防护收益。
## 当前边界
这套改动依然无法彻底防住以下情况:
-- 通过 Network 面板或抓包获取图片请求
-- 有经验的人持续分析前端运行时代码
-- 自定义 WebView、自动化脚本、Hook 等方式截获资源
-- 仅凭视觉观察后重新临摹布局和样式
-- 从本来就被允许渲染页面的客户端中提取 DOM、CSS、JS
-
-只要客户端能把页面渲染出来,客户端就一定拿到了足够还原页面的信息。
+- 通过 Network 面板或抓包获取图片请求。
+- 有经验的人持续分析前端运行时代码。
+- 自定义 WebView、自动化脚本、Hook 等方式截获资源。
+- 通过截图、录屏、最终像素提取还原视觉。
+- 人工观察后重新临摹布局和样式。
## 推荐测试方式
@@ -237,18 +163,26 @@ npm run preview
预期结果:
-- 非 App 环境下访问受保护页面会跳转到 `/#/not_app`
-- 公共页面仍可正常访问
+- 非 App 环境下访问受保护页面会跳转到 `/#/not_app`。
+- 公共页面仍可正常访问。
### 页面与图片链路验证
在接近生产环境的模式下检查:
-- `Activities` / `Ranking` 页面是否仍能正常展示
-- 受保护图片是否仍能正常显示
-- 运行时 DOM 和构建产物里是否更少直接暴露真实图链
-- 本次从 `v-show` 切到 `v-if` 的页面,是否仍能在预加载结束后正常渲染
-- 依赖触底加载的页面,滚动加载是否仍然正常
+- `Activities` / `Ranking` 页面是否仍能正常展示。
+- 受保护图片是否仍能正常显示。
+- 运行时 DOM 和构建产物中是否更少直接暴露真实图链。
+- 替换同名图片时,`assetVersions` 是否能让用户看到新图。
+
+### 性能验证
+
+重点在低端安卓 WebView 上检查:
+
+- 首屏打开速度。
+- 背景图片加载是否正常。
+- 滑动是否流畅。
+- 弹幕、跑马灯等持续动画是否造成明显卡顿。
### 构建产物验证
@@ -260,8 +194,7 @@ npm run build
预期结果:
-- `dist/js` 文件名以哈希为主
-- `dist/css` 文件名以哈希为主
-- 构建产物文件名中不再直观出现页面名
-- 生产包中不再保留 `console.log`、`console.debug`、`console.info` 和 `debugger`
-
+- `dist/js` 文件名以 hash 为主。
+- `dist/css` 文件名以 hash 为主。
+- 构建产物文件名中不再直观出现页面名。
+- 生产包中不再保留 `console.log`、`console.debug`、`console.info` 和 `debugger`。
diff --git a/docs/安全/前端加固-中文更新.md b/docs/安全/前端加固-中文更新.md
index f09a3b7..d0e155e 100644
--- a/docs/安全/前端加固-中文更新.md
+++ b/docs/安全/前端加固-中文更新.md
@@ -1,44 +1,48 @@
# 前端加固增量记录
-这份文档用于补充 [前端加固-中文.md](/D:/programs/likei-h5/docs/安全/前端加固-中文.md) 在最近一轮 `Activities / Ranking` 加固中的新增改动。
+这份文档用于补充 [前端加固-中文.md](/D:/programs/likei-h5/docs/安全/前端加固-中文.md) 在最近几轮 `Activities / Ranking` 加固和性能优化中的新增改动。
## 本轮补充
-- 生产构建混淆进一步收紧,`esbuild` 在生产相关模式下显式开启了更强的标识符、语法和空白压缩,并去除了合法注释输出。相关文件在 [vite.config.js](/D:/programs/likei-h5/vite.config.js)。
-- 公共背景组件 [BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue) 新增了 `useCanvas` 模式,装饰性多层背景会优先绘制到单个 canvas,而不是在 DOM 中直接暴露多张 `
`。
-- `canvas` 背景已扩展到更多高风险页面:
- [LesserBairam/index.vue](/D:/programs/likei-h5/src/views/Activities/LesserBairam/index.vue)、
- [LoginReward/index.vue](/D:/programs/likei-h5/src/views/Activities/LoginReward/index.vue)、
- [LuckyDollars/Season3/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season3/index.vue)、
- [LuckyDollars/Season4/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season4/index.vue)、
- [SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue)、
- [Ranking/Couple/index.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/index.vue)、
- [Ranking/GamesKing/index.vue](/D:/programs/likei-h5/src/views/Ranking/GamesKing/index.vue)、
- [Ranking/KingAndQueen/TopList.vue](/D:/programs/likei-h5/src/views/Ranking/KingAndQueen/TopList.vue)、
- [Ranking/Overall/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Overall/Ranking.vue)、
- [Ranking/WeeklyStar/WeeklyStar.vue](/D:/programs/likei-h5/src/views/Ranking/WeeklyStar/WeeklyStar.vue)。
-- 第二轮语义类名收口已经继续推进,当前已覆盖:
- [Ranking/Couple/index.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/index.vue)、
- [LuckyDollars/Season4/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season4/index.vue)、
- [SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue)、
- [SpringFestival/Ranking.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Ranking.vue)、
- [Ranking/Overall/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Overall/Ranking.vue)、
- [Ranking/WeeklyStar/WeeklyStar.vue](/D:/programs/likei-h5/src/views/Ranking/WeeklyStar/WeeklyStar.vue)。
-- [SpringFestival/Ranking.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Ranking.vue) 已额外做了时序加固:
- 组件会在 `nextTick()` 之后再注册 `IntersectionObserver`,并在 `onUnmounted` 时执行 `disconnect()`。
+- 生产构建混淆进一步收紧,`esbuild` 在生产相关模式下显式开启了更强的标识符、语法和空白压缩,并移除了合法注释输出。相关文件在 [vite.config.js](/D:/programs/likei-h5/vite.config.js)。
+- 第二轮语义类名收口已继续推进,覆盖了 `Ranking/Couple`、`LuckyDollars/Season4`、`SpringFestival`、`Ranking/Overall`、`Ranking/WeeklyStar` 等页面。
+- [SpringFestival/Ranking.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Ranking.vue) 已做时序加固:组件会在 `nextTick()` 后再注册 `IntersectionObserver`,并在 `onUnmounted` 时执行 `disconnect()`。
- [Ranking/Couple/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/Ranking.vue) 已清理上一轮遗留的乱码注释,并统一替换为中文注释。
-- [BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue) 的 `useCanvas` 继续保持显式开启,而没有改成默认值 `true`。
-- [protectedAssets.js](/D:/programs/likei-h5/src/utils/protectedAssets.js) 中的 `toCssBackgroundImage()` 作用已明确:
- 它负责把 `likei-protected:` 地址还原为 CSS 可直接使用的 `url(...)` 字符串,主要用于必须保留 `background-image` 的内联样式场景。
+- [protectedAssets.js](/D:/programs/likei-h5/src/utils/protectedAssets.js) 中的 `toCssBackgroundImage()` 作用已明确:负责把 `likei-protected:` 地址还原为 CSS 可直接使用的 `url(...)` 字符串,主要用于必须保留 `background-image` 的内联样式场景。
+
+## Canvas 背景方案调整
+
+之前 [BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue) 增加过 `useCanvas` 模式,用于把多张装饰背景绘制到单个 canvas 中,减少在 Elements 面板里直接看到背景 `
` 列表的便利性。
+
+经过低端机性能排查后,该方案已取消:
+
+- `BackgroundLayer` 仍保留 `useCanvas` 入参,兼容历史页面调用。
+- 当前实现不再根据 `useCanvas` 生成 canvas。
+- 背景图统一通过普通 `
` 渲染。
+- 图片仍继续走受保护路径解析和图片缓存链路。
+
+取消原因:
+
+- 大尺寸 canvas 会增加内存占用和绘制成本。
+- 在低端安卓 WebView 上,canvas 背景和弹幕、跑马灯、头像列表同时出现时容易放大卡顿。
+- canvas 只能降低从 DOM 面板直接复制背景结构的便利性,防不住抓包、运行时 Hook 或最终像素提取。
+- 相比这点有限收益,页面打开速度和滑动/动画流畅度优先级更高。
+
+后续约定:
+
+- 不再把 canvas 背景作为默认防扒取方案。
+- 新活动页可以继续写 `useCanvas`,但它只是兼容字段,不再产生 canvas。
+- 防扒取主要依赖受保护路径、图片缓存 blob/object URL、构建产物混淆、资源 hash 和服务端/CDN 权限策略。
## 当前边界
-- 这轮改动依然属于“提高门槛”,不是“彻底防住”。
-- `canvas` 能降低从 Elements 面板直接抄背景结构的便利度,但防不住抓包、运行时 Hook 或最终像素提取。
-- 类名和结构收口能降低产物可读性,但不能阻止有经验的人在页面完全渲染后继续还原布局。
+- 这轮改动仍然属于“提高门槛”,不是“彻底防住”。
+- 类名和结构收口能降低产物可读性,但不能阻止有经验的人在页面完整渲染后继续还原布局。
+- 前端无法阻止通过 Network 面板、抓包、运行时 Hook、截图或像素提取拿到最终资源和视觉结果。
## 验证建议
- 使用 `npm run build` 验证生产包可正常构建。
-- 使用 `npm run dev:prod` 或 `npm run preview` 验证活动页、榜单页在生产态逻辑下仍能正常显示。
-- 重点检查 `SpringFestival`、`Season4`、`WeeklyStar`、`Overall` 这些近期改动较多的页面。
+- 使用 `npm run dev:prod` 或 `npm run preview` 验证活动页、榜单页在生产态逻辑下仍能正常展示。
+- 重点检查 `LuckyDollars`、`Couple`、`LesserBairam`、`GamesKing`、`WeeklyStar`、`Overall` 这些近期改动较多的页面。
+- 在低端安卓 WebView 上验证首屏打开、背景加载、滑动、弹幕和跑马灯是否流畅。
diff --git a/docs/安全/前端加固.md b/docs/安全/前端加固.md
index b2aa852..222143d 100644
--- a/docs/安全/前端加固.md
+++ b/docs/安全/前端加固.md
@@ -144,15 +144,15 @@ Extra handling was added where needed:
- pages with `IntersectionObserver` targets now register observers only after `await nextTick()`
- preload completion still clears `isLoading` in `finally`, so preload failures do not leave the page permanently blank
-### 8. Canvas-rendered decorative background stacks
+### 8. Decorative background stacks
-The shared background layer component supports a canvas rendering mode for stacked background images.
+The shared background layer component previously supported a canvas rendering mode for stacked background images. This mode has now been disabled because large canvas backgrounds can increase memory and rendering pressure on low-end WebViews.
Main file:
- [src/components/BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue)
-Current high-risk pages using this mode:
+Pages may still pass `useCanvas`, but the prop is now kept only for compatibility:
- [src/views/Activities/LesserBairam/index.vue](/D:/programs/likei-h5/src/views/Activities/LesserBairam/index.vue)
- [src/views/Activities/LoginReward/index.vue](/D:/programs/likei-h5/src/views/Activities/LoginReward/index.vue)
@@ -167,9 +167,9 @@ Current high-risk pages using this mode:
Behavior:
-- stacked decorative backgrounds are drawn into a single canvas instead of a readable `
` list
-- most page-level decorative background structure becomes less obvious in Elements inspection
-- protected asset resolution still goes through the existing runtime/cache pipeline
+- stacked decorative backgrounds are rendered as normal `
` elements through `v-smart-img`
+- image loading still goes through protected asset resolution and the existing runtime/cache pipeline
+- canvas background rendering is no longer used as a hardening layer because the UX and memory cost is higher than the practical protection gain
### 9. Stronger production minification and selector cleanup
@@ -196,7 +196,7 @@ Recent cleanup also covered:
Behavior:
- garbled comments in `Ranking/Couple/Ranking.vue` were cleaned up and unified into Chinese comments
-- `useCanvas` remains an explicit opt-in instead of becoming a global default
+- `useCanvas` remains accepted for compatibility, but `BackgroundLayer` ignores it and renders image backgrounds
- `toCssBackgroundImage()` stays as the CSS bridge for protected asset URLs
## What This Helps Against
@@ -263,4 +263,3 @@ Expected:
- `dist/css` filenames are hash-based
- no obvious page names appear in bundle filenames
- production bundles no longer retain `console.log`, `console.debug`, `console.info`, or `debugger`
-
diff --git a/docs/活动页/竞品H5与性能优化分析.md b/docs/活动页/竞品H5与性能优化分析.md
new file mode 100644
index 0000000..3626a33
--- /dev/null
+++ b/docs/活动页/竞品H5与性能优化分析.md
@@ -0,0 +1,345 @@
+# 竞品 H5 与本项目性能优化分析
+
+## 背景
+
+本次主要排查 `src/views/Activities` 和 `src/views/Ranking` 下活动页、榜单页在低端安卓 WebView 中打开和运行卡顿的问题。测试现象中,2GB 内存低端机在展示弹幕 `Barrage`、滚动文本、跑马灯等持续动画时明显掉帧,离开这些区域后流畅度恢复。
+
+本次同时参考了两类竞品 H5:
+
+- Yalla / qrsshine 活动页:
+ - `activity2.yalla.live/activity/2025dist/Thursday`
+ - `activity2.yalla.live/activity/2025dist/sea2`
+ - `activity2.yalla.live/activity/2025dist/userCarnival2`
+ - `activity2.qrsshine.com/activity/2024dist/activityMap`
+ - `activity2.qrsshine.com/activity/2024dist/roomHonorSystem`
+- Hawatalk 活动页:
+ - `cdn.hawatalk.com/activitys/hexactivityprojectv3/guild-world-peak`
+ - `cdn.hawatalk.com/activitys/hexactivityprojectv3/miss-pk-2026`
+ - `cdn.hawatalk.com/activitys/hexactivityprojectv3/dynamic-user-id`
+ - `cdn.hawatalk.com/activitys/hexactivityprojectv3/recharge-activity`
+ - `cdn.hawatalk.com/activitys/hexactivityprojectv3/tower-tycoon`
+ - `cdn.hawatalk.com/activitys/hexactivityprojectv3/predator-journey`
+
+## 竞品 H5 的主要特点
+
+### 1. 活动独立包更明显
+
+竞品活动页大多是独立活动目录,HTML 很薄,活动自身 JS 较小。Hawatalk 新旧活动页中,活动入口 JS 通常只有几十 KB 到八十多 KB,公共 vendor 大约两百多 KB。Yalla 部分页面 vendor 较大,但也是按活动目录独立部署。
+
+这种方式的好处是:
+
+- 单个活动的首屏代码体积更可控。
+- 活动之间互相影响小。
+- 活动资源可以按目录独立缓存和更新。
+
+### 2. 静态资源缓存策略更稳定
+
+竞品普遍使用内容 hash 文件名。HTML 短缓存或不缓存,JS/CSS/图片等静态资源长缓存。这样普通发版时,只要资源内容不变,用户端就能继续命中缓存。
+
+我们的项目之前在构建文件名里带 `buildTimestamp`,每次构建都会导致 JS/CSS 文件 URL 改变,哪怕内容没变也会让缓存失效。这个问题已在本次优化中处理。
+
+### 3. 动画和视觉效果相对克制0
+
+竞品页面也会有活动视觉、排行列表和特效,但多数页面不会在首屏同时叠加大量持续动画、canvas 背景、弹幕、跑马灯、模糊滤镜和大批图片解码。
+
+本项目部分页面中,持续动画与图片层叠加较多,在高端机上影响不明显,但低端 WebView 中会明显放大。
+
+## 我们项目的主要差异与问题
+
+### 1. 持续动画叠加较多
+
+在 `src/views/Activities` 和 `src/views/Ranking` 中,存在多类持续动画:
+
+- 弹幕 `Barrage`
+- tab 文字滚动
+- 奖励列表跑马灯
+- 抽奖滚动动画
+- 倒计时定时器
+
+其中弹幕和跑马灯最容易在低端机上触发掉帧,因为它们会持续制造合成层,并且通常伴随头像、奖品图、渐变文字和透明背景。
+
+### 2. `backdrop-filter` 成本高
+
+部分页面使用了 `backdrop-filter: blur(...)`,例如:
+
+- `src/views/Ranking/Couple/components/Barrage.vue`
+- `src/views/Activities/LuckyDollars/Season5/components/Barrage.vue`
+- 多个弹窗、卡片、奖励区域
+
+`backdrop-filter` 在低端安卓 WebView 上成本很高。它会让浏览器每帧采样并合成背景,和弹幕/跑马灯一起出现时尤其容易卡顿。
+
+### 3. canvas 背景内存压力大,已取消该方案
+
+`BackgroundLayer` 曾支持 `useCanvas=true`,会先通过图片缓存加载图片,再把多张背景图绘制到一个 canvas 上。这个设计主要用于提高直接扒取 DOM 背景结构的门槛。
+
+经过低端机性能排查后,该方案已取消:
+
+- 页面继续保留 `useCanvas` 入参,兼容历史调用。
+- 当前 `BackgroundLayer` 不再生成 canvas。
+- 背景图统一通过 `
` 渲染。
+- 图片仍继续走受保护路径解析和图片缓存链路。
+
+取消原因是大尺寸 canvas 会占用较高内存和 GPU 资源。如果页面同时有弹幕、跑马灯、头像列表,就容易出现明显卡顿。canvas 对防扒取只有“提高一点直接复制 DOM 的门槛”的作用,防不住抓包、运行时 Hook 或最终像素提取,因此优先保证页面流畅度。
+
+### 4. 非关键图片预加载过早
+
+多个页面 mounted 后立即调用 `preloadOtherImages()`。虽然缓存能减少重复网络下载,但打开页面时立即预加载非关键图片,会和首屏渲染、背景绘制、接口返回后的列表渲染、弹幕动画抢资源。
+
+因此缓存不是越激进越好。缓存能减少网络重复下载,但不能减少图片解码、DOM 渲染和 GPU 合成成本。
+
+### 5. 部分隐藏内容仍然挂载
+
+一些页面用 `v-show` 切换大模块。`v-show` 只是隐藏,不会卸载组件。隐藏模块中的 DOM、图片、定时器、观察器仍可能占用内存和运行成本。活动页/榜单页中如果大模块较多,低端机压力会更明显。
+
+## 本次已经完成的优化
+
+### 1. 构建文件名改为内容 hash
+
+修改文件:
+
+- `vite.config.js`
+
+已移除构建文件名中的 `buildTimestamp`,改为内容 hash:
+
+- `js/[hash].js`
+- `css/[hash].[ext]`
+- `assets/[hash].[ext]`
+
+这样普通发版时,内容没变的 JS/CSS/图片 URL 不会改变,能继续命中浏览器/CDN 缓存。
+
+### 2. Nginx 静态资源长缓存
+
+修改文件:
+
+- `nginx/default.conf`
+- `nginx/default_hooka.conf`
+
+静态资源长缓存范围从 JS/CSS 扩展到图片、字体、视频等:
+
+- `js`
+- `css`
+- `png`
+- `jpg`
+- `jpeg`
+- `gif`
+- `webp`
+- `svg`
+- `ico`
+- `ttf`
+- `otf`
+- `woff`
+- `woff2`
+- `mp4`
+
+缓存头为:
+
+```nginx
+Cache-Control "public, max-age=2592000, immutable"
+```
+
+### 3. 图片缓存容量下调并支持单图版本
+
+修改文件:
+
+- `src/config/imageCacheVersion.js`
+- `src/config/imagePaths.js`
+- `src/utils/image/imageCacheManager.js`
+
+缓存容量从最多 200 张、200MB 调整为:
+
+```js
+cacheLimits: {
+ maxItems: 120,
+ maxSizeMB: 80,
+}
+```
+
+原因是低端机内存有限,过大的本地图片缓存会增加内存和存储管理压力。
+
+同时新增 `assetVersions` 单图/目录版本机制:
+
+```js
+assetVersions: {
+ 'Activities/LuckyDollars/Season6/bg0.png': '20260514',
+}
+```
+
+这会让该图片 URL 自动加上 `?iv=20260514`。用户端不会继续命中旧图缓存,也不需要全量清空所有图片缓存。
+
+使用建议:
+
+- 普通发版:不要改全局 `version`。
+- 单张图片替换且不改文件名:只在 `assetVersions` 中加或改该图片版本。
+- 大批量图片迁移、缓存污染:才更新全局 `version`。
+- `updateTime` 只作为人工记录,不参与实际缓存命中逻辑,不是每次改 `assetVersions` 都必须更新。
+- 单图版本不要下次发版立刻删除,至少保留超过静态资源最长缓存周期,当前建议不少于 30 天。
+
+### 4. 图片预加载改为限流和 idle
+
+修改文件:
+
+- `src/utils/image/imagePreloader.js`
+
+优化点:
+
+- 默认并发限制为 3。
+- 去重空值和重复 URL。
+- 支持 `preloadImagesIdle()`。
+- 单张图片失败不阻塞整个预加载。
+
+页面中非关键图片预加载已改为 idle:
+
+- `src/views/Ranking/Couple/index.vue`
+- `src/views/Ranking/GamesKing/index.vue`
+- `src/views/Activities/LesserBairam/index.vue`
+- `src/views/Activities/SpringFestival/index.vue`
+- `src/views/Activities/LuckyDollars/Season3/index.vue`
+- `src/views/Activities/LuckyDollars/Season4/index.vue`
+- `src/views/Activities/LuckyDollars/Season5/index.vue`
+
+### 5. App 先挂载,再后台检查版本
+
+修改文件:
+
+- `src/main.js`
+
+之前版本检查会在 app mount 前执行,可能影响打开速度。现在先挂载页面,再通过 idle/timeout 后台检查版本。
+
+### 6. 新增通用低端机性能模式
+
+新增文件:
+
+- `src/utils/performanceMode.js`
+
+低端机判断不针对单一型号,而是通用条件:
+
+- `navigator.deviceMemory <= 2`
+- 安卓低核心设备
+- 旧安卓低核心 WebView
+- 用户系统开启 `prefers-reduced-motion`
+
+启动时会给 `html` 添加:
+
+- `low-performance-mode`
+- `reduce-motion-mode`
+
+这些 class 用于全局 CSS 和组件内降级。
+
+### 7. 取消 canvas 背景渲染
+
+修改文件:
+
+- `src/components/BackgroundLayer.vue`
+
+当前即使页面传入 `useCanvas=true`,也会渲染为普通图片背景。这样仍然走图片缓存和受保护路径解析,但避免大 canvas 带来的额外内存和绘制成本。
+
+`useCanvas` 入参暂时保留,避免大量历史页面需要同步改模板;后续新页面不再依赖它做防扒取。
+
+### 8. `v-smart-img` 低端机关闭淡入动画
+
+修改文件:
+
+- `src/directives/smartImage.js`
+
+低端机下关闭图片 opacity fade 动画,并设置:
+
+```js
+el.decoding = 'async'
+```
+
+这样可以减少图片加载时的主线程和合成压力。
+
+### 9. 全局低端机移除高成本模糊和跑马灯动画
+
+修改文件:
+
+- `src/styles/global.css`
+
+低端机下统一移除:
+
+```css
+backdrop-filter
+-webkit-backdrop-filter
+```
+
+并暂停 `.scroll-wrapper` 这类持续跑马灯动画。系统开启减少动画时,也会暂停弹幕和滚动动画。
+
+### 10. 弹幕组件专项优化
+
+修改文件:
+
+- `src/views/Activities/LuckyDollars/Season3/components/Barrage.vue`
+- `src/views/Activities/LuckyDollars/Season4/components/Barrage.vue`
+- `src/views/Activities/LuckyDollars/Season5/components/Barrage.vue`
+- `src/views/Ranking/Couple/components/Barrage.vue`
+
+优化点:
+
+- 低端机弹幕最大同时显示数降到每轨 1 条。
+- 低端机弹幕生成间隔至少 4500ms。
+- 页面隐藏时暂停弹幕并清空当前动画 DOM。
+- 弹幕区域离开视口时暂停并清空当前动画 DOM。
+- 去掉 `watch(..., { deep: true })`,避免深度监听大数组。
+- 弹幕文字在入轨时预计算,减少模板每次渲染重复计算。
+- 动画结束时直接从轨道移除,不再先标记 completed 再由 interval 清理。
+- Couple 弹幕在低端模式下隐藏中间徽章图,减少一张持续参与动画的图片。
+
+## 后续建议
+
+### 1. 优先把大模块从 `v-show` 改为按需挂载
+
+活动页中排行榜、玩法、奖励、弹窗等大模块,如果不是首屏必须展示,建议逐步改成:
+
+- 首次进入时 `v-if` 挂载。
+- 数据和图片进入模块时再加载。
+- 离开模块后保留必要状态,释放重图片 DOM。
+
+本次已按体验权衡做了选择性调整:
+
+- 高频按钮、轻量图片状态仍保留 `v-show`,避免点击切换时闪烁。
+- 会触发接口、预加载、定时器或 `IntersectionObserver` 的大组件改为 `v-if`。
+- 已调整 `Ranking/Couple` 的 `Ranking` / `GamePlay` 一级模块。
+- 已调整 `Ranking/Couple/GamePlay` 内的 `lottery` / `signIn` / `exchange` 子模块。
+- 已调整 `SpringFestival` 的 `Task`、`Ranking`、`RankingReward`、`RechargeReward` 模块。
+
+### 2. 继续压缩活动页图片
+
+建议优先处理:
+
+- 背景长图
+- 弹窗背景
+- 奖励图
+- 大面积 PNG
+
+能用 WebP 的尽量用 WebP。静态装饰图可以做多尺寸或压缩版,低端机使用轻量图。
+
+### 3. 跑马灯文本尽量避免模板内测量 DOM
+
+例如 `isTextOverflow(text)` 这类函数,如果在模板中直接调用,并且内部创建 DOM + `getBoundingClientRect()`,容易触发布局计算。建议后续改成:
+
+- 数据变化后统一测量一次。
+- 缓存结果。
+- 低端机直接省略号展示,不滚动。
+
+### 4. 弹幕建议抽成公共组件
+
+现在 Season3/4/5 和 Couple 都有独立弹幕组件。后续可以抽一个公共 `PerformanceBarrage`,不同活动只传渲染内容和样式配置,避免同类性能修复重复做多次。
+
+### 5. 真机验证顺序
+
+建议在低端安卓 WebView 上按下面顺序对比:
+
+1. 打开 LuckyDollars Season5 首页,观察弹幕区域 FPS。
+2. 进入/离开弹幕区域,确认离开视口后不再持续掉帧。
+3. 打开 Couple 首页,观察告白墙弹幕和顶部跑马灯。
+4. 切换 Ranking / GamePlay,观察内存是否持续上涨。
+5. 冷启动和二次打开分别测试,确认缓存命中后网络变少,同时页面不卡顿。
+
+## 构建验证
+
+已使用项目依赖执行生产构建,构建通过。
+
+仍保留两个历史警告,和本次优化无直接关系:
+
+- `Azizi/secondBg.png` 构建时未解析,会保留到运行时解析。
+- `src/utils/http.js` 同时被静态和动态导入,无法单独拆到动态 chunk。
diff --git a/src/components/BackgroundLayer.vue b/src/components/BackgroundLayer.vue
index 70a0b86..f97c6a2 100644
--- a/src/components/BackgroundLayer.vue
+++ b/src/components/BackgroundLayer.vue
@@ -1,14 +1,6 @@
-