Repository navigation
Conversation
registerRoute() takes a new `keepAlive` option (true = the component's own name, or an explicit name), stored in the route's meta.keepAlive like the file-based pages do. The names are tracked in a reactive registry that DwcRouterView reads, so a page registered after the router view was created is cached too (it used to snapshot router.getRoutes() once at setup). unregisterRoute() withdraws the request. The route override dispatcher now takes the name of the page it wraps when that page is kept alive, so overriding the Explorer no longer stops it being cached. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
chrishamm
left a comment
There was a problem hiding this comment.
The shape is right: opt-in, one meta.keepAlive field for both route kinds, and the
reactive include list. vue-tsc is clean here too. Three things before this goes in.
resolveKeepAliveName mishandles defineAsyncComponent. The wrapper's own name is
"AsyncComponentWrapper", so keepAlive: true passes the check and puts that string
in the include list, while matches an async child against
vnode.type.__asyncResolved, so nothing is cached and nothing warns. The reverse
case is rejected: keepAlive: "MyPage" on a wrapper trips the mismatch branch,
although that name is exactly what matches once the loader resolves. Treat a wrapper
like a loader function, take a given string as-is there, and have true warn that a
lazy component needs an explicit name.
The warning string needs splitting in two. "give the component a name
(defineOptions({ name })) and it will not be cached until then" does not parse, and
in the mismatch branch the component does have a name, just not the one asked for.
Dispatchers leak out of the cache. Once uninstallRouteOverridesFor restores the
original component, the cached dispatcher keeps its entry, because the cache key is
the vnode type and its name is still in the include list. It renders null from then
on and there is no max on the keep-alive, so nothing collects it. A comment saying
so may be the better answer than a fix.
Please also click through the Explorer override in a browser before this is ready.
…dispatcher residue resolveKeepAliveName treated a defineAsyncComponent wrapper as an ordinary component. Its own name is "AsyncComponentWrapper", so keepAlive: true put that in the include list (<keep-alive> matches the resolved component, so nothing was cached and nothing warned) and keepAlive: "MyPage" was rejected as a mismatch. A wrapper (recognised by __asyncLoader, as Vue does) now counts as lazy like a loader function: a string is taken as given, true is refused with a warning that says why. The warning is now one message per case (no name / name mismatch / lazy) instead of one that read wrongly in the mismatch case. The route override dispatcher keeps the cache entry it gets by sharing the page's name after the override is dropped. Measured in Chrome: the override component itself is released (the dispatcher re-renders to nothing when the layout goes), and what stays is one empty component per registerLayout/unregisterLayout cycle, and only while the shell hosting the <keep-alive> outlives the unregistration. Documented at the dispatcher rather than fixed: reusing one dispatcher per path would leave it showing a stale null on re-install, since the override map is not reactive. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Thanks, all three points were right. Pushed e82bd8a. defineAsyncComponent. Confirmed against Vue 3.5.43: the wrapper is named Warning text. Split into three messages: no name, name differs from the one asked for, and lazy component given Dispatcher leak. I went with the comment, after measuring it rather than assuming. In Chrome, with a layout registered and unregistered four times on the Explorer path while the built-in shell stayed mounted, the Browser check. Ran against the DWC dev server in Chrome, registering through
I did not load the stock Explorer page against a live machine, only that the override path and the built-in shell behave. |
Summary
Lets a plugin's pages be kept alive while the user visits other pages, the way DWC's own Explorer already is.
registerRoute()takeskeepAlive?: boolean | string.trueuses the component's own name (name, or the__namea<script setup>SFC gets from its file); a string names it explicitly and must equal the component's name. It is stored inmeta.keepAlive, the field the file-based pages already use in their<route>block, soDwcRouterViewand a custom layout's own router view treat both alike. A route whose component has no matching name is not cached and DWC logs a warning (anincludethat matches nothing would otherwise silently cache nothing, or the wrong component).DwcRouterViewreads the names reactively. It used to snapshotrouter.getRoutes()once at setup, so a route registered afterwards (a plugin loading late, or a page created at runtime) could never be cached.plugins/index.tsnow keeps a reactive name -> count map (counted because one component can serve several routes),getKeepAliveNames()exposes it, andunregisterRoute()withdraws the request.RouteOverrideDispatcherhid the page behind it from<keep-alive>, so overriding the Explorer (registerLayout({ routes })) stopped the stock page being cached. The dispatcher now takes the name inmeta.keepAlivewhen the page has one (a string, so it works even while the page is still a lazy loader), and the override is cached exactly when the page would have been.PLUGINS.md(registerRoute) andCUSTOM-LAYOUT.md(overrides).Off by default; nothing changes for a route that doesn't ask.
Motivation
A plugin that shows an editor (or any page holding state the user would be upset to lose) currently loses it on every trip to another page, and there is no supported way to avoid that:
registerRoutehas no keep-alive option, the include list was fixed at setup, and an overridden page lost its own caching. Flexible Layouts works around this today by moving its Explorer's open tabs and unsaved buffers into a module-level store; with this it could simply keep its pages alive on builds that support it.Notes for review
<keep-alive>matches by component name, so a name has to be unique among pages. Documented inPLUGINS.md.onActivated/onDeactivatedand to make route watchers ignore changes while the route isn't the component's own (the stock Explorer does this withisOnExplorerRoute()).:keyso several routes sharing one component (Flexible Layouts' custom pages) each get their own cached instance instead of one shared one, and amaxon<keep-alive>.Testing
vue-tsc --noEmit: no errors in the changed files.includeafter the host was created still caches a later mount; without the name the component remounts; a dispatcher named after the page keeps whichever child it renders, and one with the generic name does not.registerRoute({ keepAlive: true })page, or an overridden Explorer, clicked through in a browser.🤖 Generated with Claude Code