💡

この記事の要点

この記事の重要ポイント

网站上的“安装应用”横幅,就像现在的新闻订阅弹窗一样。它确实方便,但太常在无视用户上下文的时刻出现。

这次更新聚焦于两个核心问题:博客的 运行时稳定性 (修复Three.js崩溃)与 用户体验 (重塑PWA流程)。

1. “先参与后安装”的PWA策略

浏览器原生提供“安装应用”功能,但有明显缺点:它往往在页面刚加载时触发,打断了用户,甚至在用户读第一句话之前就出现。

默认提示的问题

从搜索结果进入博客的用户,目的在于阅读而不是安装。这种弹窗式打断只会提高跳出率与认知负担。

解决方案:延时介入

我实现了 被动(受动)安装UI 。规则很简单: “只向真正感兴趣的用户提出建议。”

生命周期可视化

下面是决定是否显示提示的逻辑:

sequenceDiagram
 participant User
 participant Browser
 participant PWA as PWA Component

 User->>Browser: 打开博客文章
 Browser->>PWA: 触发 "beforeinstallprompt"
 PWA->>Browser: Prevent Default(阻止横幅)
 PWA->>PWA: 启动30秒计时器 ⏳

 rect rgb(30, 30, 30)
 note right of User: 正在阅读文章...
 end

 alt 30秒内离开
 PWA */}>User: 不做任何事(尊重专注)
 else 停留超过30秒
 PWA->>User: 显示“安装应用”吐司 📱
 User->>PWA: 点击安装
 PWA->>Browser: prompt()
 end
1

页面加载完成,用户开始阅读。

2

浏览器触发 `beforeinstallprompt`。通过 `preventDefault()` 阻止原生横幅。

3

启动30秒计时器,期间用户离开则不做任何事。

4

若仍在阅读,则在右下角显示克制的吐司提示。

实现细节

PWAInstallToast.astro 组件中控制事件。

src/components/pwa/PWAInstallToast.astro
// 保留事件以便稍后触发
window.addEventListener("beforeinstallprompt", (e) => {
 e.preventDefault();
 deferredPrompt = e;

 // 若最近没有关闭记录...
 if (!sessionStorage.getItem("pwa-install-dismissed")) {
 // 仅对高参与用户延迟提示(30秒)
 setTimeout(showToast, 30000);
 }
});

这一小改动让关系从“请安装!”变成了“你喜欢的话,也有应用版哦”。


2. 解决 “Module Not Defined” 崩溃

更新过程中,我遇到了一个会导致构建崩溃的严重错误。

构建错误
ReferenceError: module is not defined
 at .../node_modules/three/build/three.module.js

原因:手动别名的副作用

我曾在 astro.config.mjs 里手动添加别名,试图“优化”依赖:

// 错误配置示例
resolve: {
 alias: {
 "react": "path/to/react", // <--- 这就是崩溃原因
 "three": "path/to/three" }
}

这会强制Vite将 reactthree 解析到固定路径,导致 @react-three/fiber(依赖特定版本)期待的路径冲突。

graph TD
 subgraph "修复前(崩溃)"
 A[Astro/Vite]  */}|别名| B[React (v18.x 于 /path/A)]
 A  */}|Node 解析| C[React (v18.x 于 /path/B)]
 D[@react-three/fiber]  */} C
 style C fill:#f96,stroke:#333,stroke-width:2px,color:#fff
 style B fill:#f96,stroke:#333,stroke-width:2px,color:#fff
 end

 subgraph "修复后(生效)"
 X[Astro/Vite]  */}|去重| Y[React(单实例)]
 Z[@react-three/fiber]  */} Y
 style Y fill:#9f9,stroke:#333,stroke-width:2px,color:#000
 end

结果就是 多个React实例 被加载,或者在ESM环境中 module 引用被破坏。

解决方案:相信 dedupe

真正的解决方案比“优化”更简单:删除手动别名,交给Vite的 dedupe 设置,从而确保只打包一个库。

astro.config.mjs(已修复)
vite: {
 resolve: {
 // 手动别名完全删除!
 dedupe: ["react", "react-dom", "three"]
 },
 optimizeDeps: {
 exclude: ["amazon-paapi", "sharp"],
 include: ["@react-three/fiber", "@react-three/drei"]
 }
}

应用后,pnpm build 立刻以 Exit Code 0 成功。


3. 清理开发时的无关警告

另一个麻烦是开发时Pagefind搜索引擎不断抛出404错误。Pagefind是构建运行的静态搜索工具,因此在dev模式下没有 pagefind.js

于是,我更新了 SearchDialog.astro,在开发模式下显式跳过加载。

// SearchDialog.astro
if (!pagefind) {
 // 开发中跳过Pagefind加载,避免404
 if (import.meta.env.DEV) {
 return;
 }
 // 尝试导入...
}

结论

稳定性与UX往往不是取决于你添加了什么,而是 删除了什么 (手动别名、即时弹窗)。这些修复让HonoGear更稳定,也更尊重用户注意力。

[!TIP] 教训 :在React/Vite/Astro混合栈里遇到奇怪的“ReferenceError”或“Module not found”,先检查 astro.config.mjs。十之八九是解析冲突导致的。

接下来我会把重点放在内容扩充与新的“智能搜索”功能增强上。