首页
/ Dioxus PWA 实战指南:从自定义 index.html 到 Service Worker 与 Manifest 的完整配置

Dioxus PWA 实战指南:从自定义 index.html 到 Service Worker 与 Manifest 的完整配置

2026-09-05 12:30:29作者:齐添朝

本文基于 Dioxus 仓库中的 PWA 示例工程 examples/10-integrations/pwa 展开,讲解如何用 Dioxus 与 Dioxus CLI 构建一个可安装、可离线访问的渐进式 Web 应用(PWA)。读完本文后,你将掌握 PWA 所必需的 Service Worker 注册与 Manifest 注入方式、Dioxus.toml 中 Web 端关键配置的取值含义,以及带版本控制的缓存策略如何实现,并可以直接把该示例工程当作自己项目的 PWA 模板。

为什么 PWA 在 Dioxus 中需要额外配置

Dioxus 本身是一个跨 Web、桌面与移动端的 Rust 应用框架,其 Web 端由 CLI 负责编译产物打包与 HTML 生成。但 PWA 功能依赖浏览器的 Service Worker(服务工作者)与 Web App Manifest(清单文件)两项机制,这两者目前无法完全由纯 Rust 侧完成——官方示例也明确说明"这不是 100% 纯 Rust 实现"。因此该示例的核心思路是:

  • 在 Rust 侧只写普通的 Dioxus Web 应用(dioxus::launch);
  • 通过自定义 index.html 手动注入 Service Worker 注册脚本与 Manifest 引用;
  • 通过 public/ 静态目录提供 manifest.jsonsw.js,由 CLI 原样复制到构建产物中。

官方 README 同时指出,这个示例"非常适合作为你自己 PWA 项目的模板"。

运行前提与命令

在动手前需要安装 Dioxus CLI(不确定是否安装时可执行 cargo install dioxus-cli --locked)。随后在示例目录内:

  • dx serve:启动本地 Web 开发服务器,带热更新;
  • dx build --release:执行发布构建,生成可直接部署到任意 Web 服务器的静态产物。

工程结构与文件职责

示例工程结构如下(与 README 中描述一致):

├── Cargo.toml
├── Dioxus.toml
├── index.html // 本示例必须提供自定义 HTML,用于加载 SW 和 manifest
├── LICENSE
├── public
│   ├── favicon.ico
│   ├── logo_192.png
│   ├── logo_512.png
│   ├── manifest.json // 清单文件,可按需修改
│   └── sw.js        // Service Worker,实际项目必须自行修改
├── README.md
└── src
    └── main.rs

各文件的定位很清晰:

  • src/main.rs:最小 Dioxus 应用,仅用 dioxus::launch(app) 启动一个渲染居中欢迎文案的 app 组件,与 PWA 逻辑完全解耦;
  • Cargo.toml:依赖 dioxus = { workspace = true, features = ["web"] },即只启用 Web 平台特性;
  • Dioxus.toml:CLI 的构建与资源目录配置;
  • index.html:PWA 的"入口开关",负责注册 Service Worker、声明 Manifest;
  • public/manifest.jsonpublic/sw.js:PWA 的两块基石文件。

核心一:自定义 index.html 如何被 CLI 消费

PWA 示例中最关键的一步是提供自定义 index.html。示例中的完整内容如下:

<!DOCTYPE html>
<html>
  <head>
    <title>{app_title}</title>
    <script>
      if ("serviceWorker" in navigator) {
        navigator.serviceWorker.register("/{base_path}/assets/sw.js");
      }
    </script>
    <link rel="manifest" href="/assets/manifest.json" />
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta charset="UTF-8" />
  </head>
  <body>
    <div id="main"></div>
  </body>
</html>

其中几个占位符与路径的来历可以从 CLI 源码中得到印证:

  • {app_title} 占位符:CLI 在生成 HTML 时会将 Dioxus.toml[web.app] title 的取值替换进 <title> 标签。从源码结构看,这一行为实现在 packages/cli/src/build/web.rs 中——Self::replace_or_insert_before("{app_title}", "</title", &title, &mut html),即无论 <title> 标签是否已存在,都会替换或插入标题。本例中对应的标题值就是 Dioxus.toml 里的 title = "dioxus | ⛺"
  • {base_path} 占位符:用于支持应用部署在子路径下的场景。CLI 的 base_path 解析逻辑见 packages/cli/src/build/request.rs:优先取命令行参数 --base-path,其次回退到 Dioxus.toml[web.app] base_path 配置,未配置时按根路径处理。Service Worker 注册路径 /{base_path}/assets/sw.js 正是利用了这一点,保证 SW 文件位于 scope 可达范围内。
  • public/ 目录的资源去向Dioxus.tomlasset_dir = "public" 声明了静态资源目录,构建时 sw.jsmanifest.json、图标等文件会被复制到产物目录(通常位于 dist/assets/ 下),这就是 HTML 中以 /assets/ 前缀引用它们的原因。

<div id="main"></div> 是 Dioxus Web 端挂载虚拟 DOM 的容器,与默认 HTML 模板一致。

核心二:Dioxus.toml 的 Web 端配置

示例的 Dioxus.toml 完整内容如下,逐项说明:

[application]
# 应用(项目)名称
name = "dioxus-pwa-example"

# Dioxus 应用的默认平台
# 可选值:desktop, web, mobile, ssr
default_platform = "web"

# `build` 与 `serve` 的产物输出目录
out_dir = "dist"

# 静态资源(public)目录
asset_dir = "public"

[web.app]
# HTML 标题(即替换 {app_title} 的值)
title = "dioxus | ⛺"

[web.watcher]
# watcher 触发时是否重新生成 index.html
reload_html = true

# watcher 监控哪些文件/目录
watch_path = ["src", "public"]

几个与 PWA 场景直接相关的点:

  1. default_platform = "web":让 dx serve / dx build 默认面向 Web 平台,无需每次加 --platform 参数;
  2. asset_dir = "public":PWA 所需的 manifest.jsonsw.js 放在该目录下,即可随构建一起分发;
  3. [web.watcher] watch_path 包含 "public":开发时修改 public/ 下的静态文件(例如调整图标)也能触发热更新流程;reload_html = true 表示 watcher 触发时会重新生成 index.html——对依赖自定义 HTML 的 PWA 项目来说这是一个值得注意的行为。

核心三:manifest.json 字段逐项解析

Web App Manifest 决定应用"是否可安装"以及安装后的外观。示例 manifest.json 中出现的字段如下:

字段 示例值 含义
name "Dioxus" 应用完整名称,安装提示与主屏显示用
short_name "Dioxus" 空间受限处(如桌面图标)显示的短名
icons logo_192.pnglogo_512.png(含 "sizes": "any" 条目) 安装图标。512 图标同时声明了 "sizes": "any",用于可缩放场景(如 splash 屏)
start_url "/" 用户点击图标启动时打开的页面
id "/" 应用的唯一标识,用于区分不同版本
scope "/" 应用的"作用域",SW 与 Manifest 生效的 URL 范围
display "standalone" 以独立窗口运行,隐藏浏览器 UI
display_override ["window-control-overlay", "standalone"] 提示浏览器在支持时可叠加窗口控制条,回退到 standalone
theme_color / background_color "#000000" / "#ffffff" 工具栏主题色与启动背景色
dir / lang "ltr" / "en" 文本方向与语言
orientation "portrait" 启动时请求的屏幕方向

配套的图标文件为 public/logo_192.pngpublic/logo_512.png,外加 public/favicon.ico。README 明确提示"manifest 文件可按需自行编辑",实际项目中应替换为自己的应用名、图标与配色。

核心四:sw.js 的缓存策略逐段解读

示例 sw.js 是一份带完整注释的 Service Worker 参考实现,覆盖了 SW 生命周期的三个事件,可以逐段对照阅读:

1. 版本前缀与离线基线资源

var version = 'v1.0.0::';

var offlineFundamentals = [
  // 在此添加你想要预先缓存的文件
  'favicon.ico'
];

缓存名统一带 version 前缀(v1.0.0::fundamentalsv1.0.0::pages)。README 对此有明确提醒:sw.js 在实际项目中必须修改——你需要扩充 offlineFundamentals 清单,把真正需要离线的核心资源(HTML 入口、关键 JS/CSS 等)加入预缓存。注意其语义:安装阶段若任一资源下载失败,整个 Service Worker 安装会失败。

2. install 事件:预缓存

self.addEventListener("install", function (event) {
  event.waitUntil(
    caches.open(version + 'fundamentals')
      .then(function (cache) {
        return cache.addAll(offlineFundamentals);
      })
  );
});

event.waitUntil 阻塞安装流程直至 Promise 结算;cache.addAll 会依次请求并把 offlineFundamentals 中的所有资源写入版本化缓存。

3. fetch 事件:"缓存优先、网络刷新"策略

这是示例中最值得关注的策略,属于"progressive networking(渐进式网络)"模式:

  • 非 GET 请求直接放行到网络,交给客户端处理失败重试;
  • GET 请求先 caches.match(event.request) 查缓存;
  • 命中缓存也照样发起网络请求:把网络响应的副本(response.clone())写入 version + 'pages' 缓存,但本次响应优先返回缓存内容——即"立刻给出旧数据,后台刷新缓存",产生"最终新鲜"(eventually fresh)的响应;
  • 缓存与网络都失败时,进入 unableToResolve() 兜底,程序化返回一个 503 Service Unavailable 的 HTML 响应,保证用户始终能看到有意义的错误页,而不是浏览器默认的错误界面。

4. activate 事件:清理旧缓存

self.addEventListener("activate", function (event) {
  event.waitUntil(
    caches.keys().then(function (keys) {
      return Promise.all(
        keys.filter(function (key) {
              return !key.startsWith(version);
            }).map(function (key) {
              return caches.delete(key);
            })
      );
    })
  );
});

新 Worker 激活后,删除所有不以当前 version 为前缀的历史缓存。配合上面的版本前缀机制,升级 version 号即可让旧缓存一次性整体失效——这正是文件头注释所说"版本号在更新 worker 逻辑时很有用"的原因。

落地为生产项目的注意事项

结合示例与官方说明,把该模板用于真实项目时至少要做以下事情:

  1. 必须改写 sw.js:扩充 offlineFundamentals 预缓存清单,并按业务调整 fetch 策略(例如 API 请求可能希望"网络优先"而非"缓存优先");每次修改逻辑后递增 version
  2. 按品牌改写 manifest.json 与图标文件,start_url / scope 需与站点实际部署路径一致;若部署在子路径下,同步配置 CLI 的 base_path
  3. 保留自定义 index.html:SW 注册与 manifest 引用是 PWA 生效的前提,删除它会退化为普通 Web 应用;
  4. 若希望尽可能贴近 100% 纯 Rust,官方 README 提到可以尝试 wasi-worker 方案替换 JS 编写的 Service Worker 文件,但 JSON 清单文件仍然是必需的;
  5. 构建发布流程不变:dx build --release 产出后部署到任意静态 Web 服务器即可,PWA 的生效前提是 HTTPS(浏览器对 Service Worker 的要求)。

参考文件索引

内容 路径
示例说明(本文依据的原始文档) examples/10-integrations/pwa/README.md
自定义 HTML 模板 examples/10-integrations/pwa/index.html
CLI 配置 examples/10-integrations/pwa/Dioxus.toml
应用入口 examples/10-integrations/pwa/src/main.rs
清单文件 examples/10-integrations/pwa/public/manifest.json
Service Worker examples/10-integrations/pwa/public/sw.js
CLI 标题/base_path 处理源码 packages/cli/src/build/web.rspackages/cli/src/build/request.rs
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384