跪拜 Guibai
← All articles
Frontend · Uni-app

uni-router v1.10 Ships a Global Event Bus That Gives Every Navigation Method a Bidirectional Channel

By PedroQue99 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Any uni-app project that uses replace or relaunch for login flows, stack replacement, or app resets now gets the same inter-page communication that was previously exclusive to push. The sticky cache removes a class of flaky, timing-dependent bugs that are hard to reproduce and harder to fix without a framework-level solution.

Summary

The native uni-app EventChannel only works with navigateTo, leaving replace and relaunch navigations without a way to pass data between pages. uni-router v1.10 introduces a built-in communication manager that wraps uni.$emit/$on, assigning a unique navigationId to every navigation so push, replace, and relaunch all return a usable eventChannel. The same channel is exposed to RouterLink’s navigated event, unifying the API across every navigation method.

A sticky event cache inside UniEventChannel solves the long-standing race condition where an emit fires before the target page’s setup registers its listener. Emitted data is always cached, and any late-arriving on or once registration receives it asynchronously via a microtask. The cache persists until the channel is destroyed, so even a once listener registered later gets the last payload.

The target page retrieves its channel through a new usePageChannel() composable that reads a __nav_id from the URL query, making the channel survive H5 refreshes. When no navId is present—such as a direct browser visit—the API returns a noopChannel so callers never need null checks. Channel cleanup is automatic in onUnmounted, preventing memory leaks from abandoned listeners.

Takeaways
— Enabling useUniEventChannel: true in createRouter makes push, replace, and relaunch all return a NavigationResult with an eventChannel property.
— UniEventChannel is built on uni.$emit/$on and isolates channels by a unique navigationId, preventing cross-navigation event leakage.
— Sticky caching stores the last emit payload; any on or once listener registered later receives it via a Promise microtask, eliminating race conditions.
— usePageChannel() replaces getOpenerEventChannel() and reads __nav_id from the URL query, so the channel survives an H5 page refresh.
— When no __nav_id is present, usePageChannel() returns a noopChannel whose methods are safe no-ops, removing the need for null checks.
— RouterLink’s @navigated event now fires for all navigation types and passes the eventChannel, unifying the template API.
— Channel cleanup runs automatically in onUnmounted, destroying listeners and caches to prevent memory leaks.
— The release is backward compatible; without useUniEventChannel, behavior matches v1.9.0 exactly.
Conclusions

Sticky caching that never deletes until channel destruction is a deliberate trade-off: it guarantees delivery to late-arriving once listeners but means the cache holds a reference to the last payload for the channel’s entire lifetime.

Passing __nav_id through the URL query mirrors the existing __params_key pattern, which is a pragmatic choice for H5 survival but exposes an internal routing detail in the address bar.

The first-wins strategy in the channel registry prevents a second navigation to the same navId from overwriting an existing channel, which protects against accidental double-registration but could mask bugs where duplicate navigations occur.

Concepts & terms
Sticky event cache
A cache that retains the last emitted payload for a given event name. When a listener registers after the emit, the cache replays the payload asynchronously via a microtask, preventing data loss from timing race conditions.
noopChannel
A sentinel object implementing the EventChannel interface where every method is a no-op that returns itself. It is returned by usePageChannel() when no navigation channel exists, so calling code never needs to check for null or undefined.
navigationId
A unique identifier generated per navigation (format nav-<timestamp>-<seq>) that prefixes event names on the global bus, isolating events between different navigations so they do not interfere with each other.
From the discussion

A preference surfaces for Flutter's promise-based navigation pattern, where a push returns a result directly, over the event-bus workaround uni-router provides. The limitation is acknowledged as a platform constraint — uni-app's own API simply doesn't offer that capability.

— Flutter's await navigator.push() pattern, which returns a result directly, is seen as a cleaner alternative to the event-bus approach.
— The uni-app framework's native API lacks support for promise-returning navigation, making the event-bus workaround a necessary adaptation rather than a design choice.
Featured comments
似水流年146

More inclined toward the Flutter approach, const result = await navigator.push()

似水流年146

result can then receive the returned parameters

PedroQue99  → 似水流年146

There's no way around it, the uni-app official docs don't support that kind of API [facepalm]

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗