FrankenPHP 热重载(Hot Reload)完整指南:让 PHP 应用开发告别手动刷新
发布时间:2026/9/16 0:28:01 作者:尧图编辑部 阅读量:1,286
完整指南:让 PHP 应用开发告别手动刷新)
FrankenPHP 热重载Hot Reload完整指南让 PHP 应用开发告别手动刷新【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphpFrankenPHP 内置了面向开发场景的**热重载Hot Reload**功能它借鉴了 Vite、webpack 等现代前端工具中 Hot Module ReplacementHMR的交互体验当 PHP 代码、模板、JavaScript、CSS 等文件发生变化时浏览器页面内容会被实时更新无需手动刷新。阅读完本文你将掌握在 Caddyfile 中启用hot_reload的完整配置方法含短格式与长格式、客户端集成方式、与 Worker 模式的组合用法以及该功能从文件监听、Mercure 推送、DOM 变换到页面刷新的完整实现链路。Hot reload 是什么PHP 世界的 HMR在传统的 PHP 开发流程中每修改一次代码PHP 逻辑、模板、前端资源……都需要手动切回浏览器刷新页面才能看到改动效果。FrankenPHP 的hot reload特性正是为了大幅改善这一开发体验而设计它提供了与 Vite、webpack 等现代 JavaScript 工具链中Hot Module ReplacementHMR类似的工作流让页面内容随文件变更实时更新。该特性原生支持 WordPress、Laravel、Symfony 以及任何其他 PHP 应用或框架不需要为特定框架编写适配代码。启用后FrankenPHP 会监听当前工作目录的文件系统变化一旦有文件被修改便向浏览器推送一条 Mercure 更新事件。根据客户端环境不同浏览器会采取两种更新策略如果页面加载了 [Idiomorph]一个 DOM 变换库则对 DOM 进行变换morph在保留滚动位置与输入框状态的前提下应用内容变化如果 Idiomorph 不存在则整页刷新standard live reload调用window.location.reload()。启用与基础配置前置条件启用 Mercure热重载依赖 Mercure 作为服务端到浏览器的实时推送通道因此必须先启用 Mercure再在php_server指令中加入hot_reload子指令。若未配置 Mercure hub配置阶段会直接报错——这一点在源码中有明确校验见 caddy/hotreload.goif f.mercureHub nil { return errors.New(unable to enable hot reloading: no Mercure hub configured) }最短配置示例localhost mercure { anonymous } root public/ php_server { hot_reload }[!WARNING]该特性仅限开发环境使用。切勿在生产环境启用hot_reload因为它不安全会暴露敏感的内部细节并且会拖慢应用性能。默认监听范围不指定任何参数时FrankenPHP 默认监听当前工作目录中匹配如下 glob 模式的所有文件./**/*.{css,env,gif,htm,html,jpg,jpeg,js,mjs,php,png,svg,twig,webp,xml,yaml,yml}该默认值定义在 caddy/hotreload.go 的常量defaultHotReloadPattern中并在configureHotReload里于监听模式列表为空时被自动填充见 caddy/hotreload.goif len(f.HotReload.Watch) 0 { f.HotReload.Watch []string{defaultHotReloadPattern} }可以看到默认模式覆盖了 PHP、Twig 模板、前后端 JS.js/.mjs、CSS、HTML、图片.gif/.jpg/.jpeg/.png/.svg/.webp以及.env、.xml、.yaml/.yml等常见开发文件类型。精细控制监听范围短格式显式指定监听模式默认的监听范围可能过宽或过窄你可以使用 glob 语法显式指定需要监听的文件。hot_reload后的剩余参数RemainingArgs会被直接解析为监听模式列表localhost mercure { anonymous } root public/ php_server { hot_reload src/**/*{.php,.js} config/**/*.yaml }上面的配置表示只监听src/下所有 PHP 与 JS 文件以及config/下的所有 YAML 文件。长格式指定 Mercure topic 与多个 watch 路径当需要进一步定制时可以使用hot_reload的长格式通过topic指定 Mercure 主题通过多条watch子指令分别监听不同的目录或文件localhost mercure { anonymous } root public/ php_server { hot_reload { topic hot-reload-topic watch src/**/*.php watch assets/**/*.{ts,json} watch templates/ watch public/css/ } }watch参数支持glob 文件模式如src/**/*.php带花括号扩展的模式如assets/**/*.{ts,json}纯目录路径如templates/、public/css/监听该目录下的所有变化。topic未指定时FrankenPHP 会基于配置生成一个唯一主题格式为https://frankenphp.dev/hot-reload/十六进制哈希见 caddy/hotreload.go 中的uniqueID逻辑。topic子指令只允许使用一次且仅接受一个值非法子指令会触发wrongSubDirectiveError(hot_reload, topic, watch, v)错误。监听模式的底层解析glob 与花括号如何被真正匹配配置中的 glob 模式并非直接交给操作系统而是由 FrankenPHP 自研的internal/watcher包解析执行这部分实现可以让你更准确地预判监听行为目录与模式的拆分模式首先被转换为绝对路径再按路径分隔符拆分区分出「固定目录部分」与「含*、?、[、{等通配符的匹配部分」见 internal/watcher/pattern.go。**递归匹配模式会按**拆分成多段逐段与文件路径从前往后最后一段从文件末尾倒序进行匹配见 internal/watcher/pattern.go。花括号扩展{php,twig,yaml}这类语法会被递归展开为多个子模式再逐一交给filepath.Match匹配见 internal/watcher/pattern.go。此外监听层还内置了**防抖debounce**机制文件事件到达后会等待150msdebounceDuration再批量触发重载回调避免保存文件时连续写入造成频繁刷新见 internal/watcher/watcher.go 与 internal/watcher/watcher.go。如果 watcher 被意外提前关闭还会自动重试监听最多重试 5 次maxFailureCount且若 5 秒内未再次失败则重置失败计数。客户端集成订阅 Mercure 事件服务端负责检测变化而浏览器需要订阅这些事件才能更新页面。FrankenPHP 通过$_SERVER[FRANKENPHP_HOT_RELOAD]环境变量暴露了用于订阅文件变化的 Mercure Hub URL。该变量在configureHotReload中注入见 caddy/hotreload.gof.preparedEnv[FRANKENPHP_HOT_RELOAD\x00] /.well-known/mercure?topic url.QueryEscape(f.HotReload.Topic)使用官方 JavaScript 库推荐社区提供了一个开箱即用的 JavaScript 库frankenphp-hot-reloadnpm 包来处理客户端逻辑。将它引入主布局main layout即可!DOCTYPE html titleFrankenPHP Hot Reload/title ?php if (isset($_SERVER[FRANKENPHP_HOT_RELOAD])): ? meta namefrankenphp-hot-reload:url content?$_SERVER[FRANKENPHP_HOT_RELOAD]? script srchttps://cdn.jsdelivr.net/npm/idiomorph/script script srchttps://cdn.jsdelivr.net/npm/frankenphp-hot-reload/esm typemodule/script ?php endif ?这里的关键点用isset($_SERVER[FRANKENPHP_HOT_RELOAD])判断热重载是否启用生产环境未启用时不会加载任何额外脚本idiomorph脚本用于启用 DOM 变换模式保留滚动位置与输入状态库会自动订阅 Mercure hub检测到文件变更后在后台 fetch 当前 URL并对 DOM 执行变换morph。保留特定 DOM 节点在某些罕见场景下例如使用 Symfony Web 调试工具栏这类开发工具时你可能希望变换 DOM 时保留特定节点不被替换。此时只需给对应 HTML 元素加上data-frankenphp-hot-reload-preserve属性div># Caddyfile组合 hot reload 与 worker watch构建完整开发工作流 localhost mercure { anonymous } root public/ php_server { hot_reload worker { file /path/to/my_worker.php watch } }关于worker.watchdocs/config.md 中还有几点值得注意未指定监听路径时默认回退到./**/*.{env,php,twig,yaml,yml}即监听进程启动目录下所有.env、.php、.twig、.yaml、.yml文件支持多次watch指定多个路径**表示递归监听路径可以是相对路径若定义了多个 worker文件变化时所有 worker 都会重启应避免监听运行时才会产生的文件如日志否则可能引发无谓的 worker 重启。两者的文件监听底层都基于同一个e-dant/watcher库FrankenPHP 为其贡献了 Go 绑定并复用internal/watcher的 PatternGroup 机制见 internal/watcher/watcher.go因此模式语法与行为完全一致。端到端验证测试用例如何证明整条链路仓库中的 caddy/hotreload_test.go 提供了一个完整的端到端测试可以作为理解整条链路的绝佳参考启动一个带mercure { anonymous }与hot_reload { topic ... watch .../*.php }的测试服务器客户端通过GET /.well-known/mercure?topicurl-encoded topic建立 SSE 订阅这正是FRANKENPHP_HOT_RELOAD环境变量指向的地址形态测试程序向被监听的目录写入一个index.php文件客户端在 SSE 流中收到包含index.php字样的事件后立即断言通过随后再次请求/index.php验证响应内容。这条测试从「监听文件写入 → 推送 Mercure 事件 → 客户端收到更新」完整覆盖了热重载的核心链路。其中服务端把事件列表序列化为 JSON 后推送到 Mercure hub 的实现位于根目录的 hotreload.goWithHotReload选项并且特意等待 worker 重启完成后再发送更新保证浏览器收到通知时后端代码已经是最新版本。五步工作流回顾整个热重载机制可以归纳为以下流程监听WatchFrankenPHP 在后台使用e-dant/watcher库监听文件系统变化并经过 150ms 防抖与 glob/花括号模式过滤重启RestartWorker 模式若 worker 配置中启用了watch则重启 PHP worker 以加载新代码并等待其完成推送Push包含被修改文件列表的 JSON 负载被发送到内置的 Mercure hub主题由topic指定默认自动生成接收Receive通过 JavaScript 库或自行订阅监听的浏览器收到 Mercure 事件更新Update若检测到 Idiomorph则 fetch 更新后的内容并对当前 HTML 执行 DOM 变换即时应用变更且不丢失页面状态否则调用window.location.reload()刷新整页。注意事项与最佳实践仅限开发环境hot_reload会暴露敏感内部细节且拖慢性能生产环境务必关闭依赖 Mercuremercure块必须在php_server之前或同站点内启用否则配置校验直接失败控制监听范围默认模式监听的文件类型较多建议在项目规模变大后改用显式watch缩小范围减少无谓的推送与重启Worker 模式必须双管齐下单独使用hot_reload或单独使用worker.watch都无法获得完整体验二者组合才能同时刷新「服务端代码」与「浏览器页面」DOM 状态保持想保留表单输入、滚动位置或调试工具节点务必引入 Idiomorph并用data-frankenphp-hot-reload-preserve标记需要保留的元素。相关文档与实现路径速查热重载英文文档 Mercure 集成 Worker 模式 全局配置含 worker.watch Caddy 模块实现 服务端推送实现 监听引擎 端到端测试【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考