监听与符号链接
插件自行管理页面内容监听。Vite 的共享 watcher 负责配置文件事件、页面目录结构通知和模块热更新,两条路径分开工作。
页面内容更新
页面文件的新增、删除、修改,以及内容 watcher 第一次 ready 后的补扫,都会重新读取页面内容。
- 同路径快速删除重建,会刷新标题、路由和
definePage(null)状态 - 普通
change只重读该物理文件关联的缓存候选,其他页面继续使用内容缓存 - 普通
change下页面元数据没变时,跳过配置加载和产物生成 - 元数据变化后,仍会扫描页面清单、校验归属与路由冲突,再合并产物
- 读取失败会保留上次成功配置,后续变更或整轮补扫可以重试
生成期间到达的事件合并到下一轮。同一批次的多个普通内容变更只读各自关联的页面;批次里包含新增、删除、目标切换或恢复等结构变化时,执行完整刷新。
编辑器用临时文件重命名覆盖页面时,文件目标的内容监听负责接续替换后的文件。原子保存产生的新增、删除事件,或后端目录通知,也可能触发来源发现或结构刷新。
符号链接
已收录的符号链接页面可以通过词法路径或真实目标路径的事件更新。事件按当前有效的词法来源检查 exclude 和扫描规则,再重新计算页面归属。物理目标的位置不改变排除规则和输出路径的基准。
例如 src/pages/feature 指向外部 shared/pages:
- 启动时目标不存在,稍后创建
shared/pages/profile.vue,页面会自动加入 - 运行中把
feature改指另一目录,新目录的后续修改也会更新产物 - 两个有效入口共用同一目标时,移除其中一个入口仍保留另一个入口的订阅
- 最后一个有效入口退出后才释放目标
页面来源目录内部的嵌套符号链接也参与发现和恢复。链接指向空目录、尚未创建的目录或另一层链接时,入口关系会保留;目标创建后开始发现页面并监听内容。嵌套链接只补充当前来源的监听依赖,不改变主包和分包的声明及输出路径。
监听目标统一换算成真实路径,再用于订阅和事件身份比较。这样同一目标的 Unicode 规范化或大小写拼写差异不会导致事件映射失败。
以下路径按“暂不可用来源”处理,本轮跳过监听,页面扫描和生成继续:
- 目标中暂时不可读的部分,例如权限不足
- 解析期间被删除或改指的链接
- 循环链接
权限恢复后,后续刷新或重新扫描会重新发现。页面监听初始化失败只记录错误,不影响生成;后续生成会重试,共享 watcher 收到页面文件事件时也会立即重试。
订阅方式
内容订阅使用解析后的物理路径,不递归跟随链接。所有有效词法来源共同决定订阅集合:
- 共享目标只订阅一次
- 父目录已经覆盖的子目录和文件目标不重复注册
- 目录之外的文件别名单独订阅实际文件
- 来源或目录身份变化时重新建立订阅,避免复用旧目标句柄
- 关闭后不再发现或订阅
恢复观察使用非递归的目录入口监听,不会把公共祖先下的兄弟项目加入递归监听。目标或来源变化后会收回不再需要的订阅。
单轮来源发现最多登记 1024 个链接来源。别名层叠会让同一物理目录经多条词法路径重复展开,达到上限后本轮停止继续展开并输出警告。页面扫描和 pages.json 生成不受影响,但超出上限的来源本轮不会建立内容监听。收窄 dir、subPackages 或排除不用的链接可以恢复完整覆盖。
Vite 配置
普通非循环链接不需要额外配置。以下两项是 Vite 配置,分别控制监听和模块解析:
| 配置 | 默认值 | 作用 |
|---|---|---|
server.watch.followSymlinks | true | 监听器是否沿符号链接覆盖目标 |
resolve.preserveSymlinks | false | 模块身份是否保留链接路径,默认使用真实路径 |
循环链接
仅当循环链接导致 Vite 共享 watcher 报 ELOOP 时,才考虑关闭链接递归:
export default defineConfig({
server: {
watch: { followSymlinks: false },
},
plugins: [UniPages()],
})followSymlinks 作用于整个共享 watcher,可能影响链接模块的 HMR 和依赖它的其他插件。调整后需要验证项目中的模块热更新。
项目使用 preserveSymlinks: true 时,关闭 followSymlinks 后要特别验证链接模块的 HMR。页面配置恢复成功不能证明模块热更新正常。
轮询
Windows 下原生监听可能持有子目录句柄,导致重命名父目录时报 EPERM。可以启用 Chokidar 的轮询后端:
CHOKIDAR_USEPOLLING=trueCHOKIDAR_INTERVAL 指定轮询间隔,单位毫秒。轮询会增加 CPU 和文件系统访问开销,插件不会默认启用。
H5 开发还支持 Vite 的 server.watch.usePolling 和 server.watch.interval。server.watch.ignored 会约束页面监听,awaitWriteFinish 和 atomic 会转发给内容 watcher。
性能
页面清单通过 glob 遍历目录,监听依赖发现另有一次目录遍历。两次覆盖同一目录树时,目录读取大约多一遍。这不代表每次保存都会完整扫描。
含 ** 的目录 glob 需要为每个可能成为页面父级的匹配目录建立非递归入口监听。可以匹配的目录越多,原生监听句柄越多;收窄 glob 前缀或排除无关目录可以减少开销。
来源发现会复用同一次遍历中的目录读取和排除匹配结果,不缓存文件系统状态,也不缓存 Vite ignored 的判断结果。判断是否跳过生成,靠的是页面元数据是否变化。