优先选 ladumor/laravel-pwa:它自带 pwa:generate 命令生成 public/sw.js,默认缓存关键静态资源,开箱即用;对 Laravel 11 + Vite 兼容性更好,sw.js 路径适配 Vite 的 base 配置,而 silviolleite/laravelpwa 不生成 sw.js 且硬编码路径为 /service-worker.js,需手动调整。

直接用 silviolleite/laravelpwa 或 ladumor/laravel-pwa 这类包,比手动配 manifest + Service Worker 快 5 倍以上,且避免 90% 的兼容性坑——尤其是 Laravel 11 默认不带 public/sw.js 和缓存策略联动。
选哪个包?silviolleite/laravelpwa vs ladumor/laravel-pwa
两个都维护活跃(截至 2026 年 5 月),但行为差异明显:
-
silviolleite/laravelpwa:生成manifest.json+ 自动注入<link rel="manifest">+ 提供 Blade 指令@pwa,但不生成或管理sw.js;适合你已用 Laravel Mix + Workbox 管理离线逻辑的项目 -
ladumor/laravel-pwa:自带php artisan pwa:generate命令,会生成public/sw.js并默认缓存/、/css/、/js/和.png/.jpg,更适合开箱即用型 PWA - 如果你用的是 Laravel 11 + Vite,优先选
ladumor/laravel-pwa——它生成的sw.js路径与 Vite 的base配置兼容性更好;silviolleite的sw.js注册逻辑硬编码为/service-worker.js,Vite 项目常需手动改路径
php artisan vendor:publish 后必须检查的三处配置
发布配置后,别直接跑命令。打开 config/pwa.php(或 config/laravelpwa.php)重点核对:
-
'name'和'short_name':必须是纯 ASCII 字符,含中文会导致 iOS Safari 安装失败(报错Failed to load manifest) -
'icons'数组里每个图标路径,必须真实存在于public/下,且尺寸严格匹配:至少包含192x192和512x512两种 PNG;少一个,Android Chrome 添加主屏幕时会回退到文字图标 -
'start_url':不能以https://开头,必须是相对路径如'/'或'/app/';否则 Service Worker 注册后跳转失败,表现为点击桌面图标白屏
Service Worker 注册失败的典型现象和修复
常见错误现象:navigator.serviceWorker.register 报 TypeError: Failed to register a ServiceWorker 或控制台显示 Uncaught (in promise) DOMException: Registration failed。
- 根本原因几乎都是路径问题:Laravel 默认不启用 HTTPS,而 Service Worker 要求在
localhost或 HTTPS 环境下运行;开发时用php artisan serve是 HTTP,必须加--host=127.0.0.1 --port=8000并访问http://127.0.0.1:8000(不是http://localhost:8000),否则注册被浏览器拒绝 - 另一个高频原因是
public/sw.js返回了 404 或 HTML(比如 Laravel 的 404 页面);用浏览器直接访问http://127.0.0.1:8000/sw.js确认返回纯 JS 内容,且响应头含Content-Type: application/javascript - 如果用了 Nginx,确认没把
.js文件重写进 Laravel 路由;检查location ~ \.js$块是否漏掉了try_files $uri =404;
离线缓存失效的隐藏陷阱
即使 sw.js 注册成功,用户切到飞行模式后仍加载失败,往往卡在这些点:
- Laravel 的
APP_URL和前端请求的域名不一致(比如APP_URL=http://test.local但你在浏览器输http://127.0.0.1:8000),导致 Service Worker 缓存的 fetch 请求被跨域拦截,静默失败 - API 路由走的是
routes/api.php,但sw.js默认只缓存静态资源;要缓存/api/posts,必须手动修改生成的sw.js或在webpack.mix.js里用runtimeCaching显式声明,不能依赖包自动生成的规则 - Blade 模板里用了动态 URL(如
{{ asset('js/app.js?id=' . time()) }}),每次刷新都变链接,Service Worker 认为是新资源,旧缓存立即失效;应禁用开发期的版本哈希,或改用 Laravel Mix 的version()方法做稳定哈希
最易被忽略的是:Laravel 的 cache 驱动设为 file 时,Cache::put() 存的数据根本不会进 Service Worker 缓存——那是服务端缓存,和服务端离线无关;PWA 离线靠的是前端 Cache API,两者完全隔离。


















