nuxt
nuxt copied to clipboard
Vue router construction should includes page's meta info
What problem does this feature solve?
Now, the page's meta info only accessable in the middlewares.
While for a static generated
site, the beforeEach()
and other mehotds in vue router
can't access the page's meta info.
It causes some inconvenience for usecases like user authtication in a fully static
or static/SPA
website without ssr.
What does the proposed changes look like?
The vue routes
construction process should includes the page's meta info.
Same issue here in SPA mode. Heres an example of getting meta property in $route, YES: https://github.com/nuxt/nuxt.js/tree/dev/examples/routes-meta
But when tring to get meta data from router guard, NO:
context.app.router.beforeEach((to, from, next) => {
// to.meta returns {} <- empty object
})
Actually it is more tricky than it looks like since we need to asynchronously resolve the components (because of code-splitting): https://github.com/nuxt/nuxt.js/blob/dev/packages/vue-app/template/utils.js#L217
You can use a global middleware to achieve the same behaviour of beforeEach
: https://nuxtjs.org/guide/routing#middleware
@Atinux well, during your routes generation, why not copy the meta of the page component at build-time to the associated route definition? Taking it statically and not dynamically, which would be dissociated from the actual loading.
You can use a global middleware to achieve the same behaviour of beforeEach: https://nuxtjs.org/guide/routing#middleware This is just avoiding an issue with a larger scope, of course a middleware would do the trick for custom code, but won't work on thrid-party plugins that actually use
$route.meta
orto.meta
internally.
I myself had the issue while using the plugin vue-auth
(@nuxtjs/auth does not fit my needs), that uses it's own router hooks internally based on to.meta
.
Other issue with having to use a middleware like you say, is that, in any case, we can't use $route.meta
in tempaltes, we can only accès it in the asyncData ({ route })
or eventually with $options.meta
inside the page component, but both of these solutions does not work in child component that needs to access the route's meta data.
A solution, as said at the begining of my answer would be to do something like:
// ... some route generation code
const PageCompOptions = comp.isSFC ? getScriptTagExport(comp) : getJSExport(comp);
route.meta = PageCompOptions.meta || {};
// ... some route generation code
Thise code would only done during boilerplate generation, not during SSR or Client of the actual app. Basically this would be dissociated from the actual: https://github.com/nuxt/nuxt.js/blob/e8aca9eb117851047e82e94948ff8b4bcb464b1a/packages/vue-app/template/router.js#L78
I've run into this problem when I needed to have some parts of a layout configured in some of the pages using it. So I tried the "meta" solution where a page defines the layout and gives some config info in the meta
option of the vue component. The layout makes use of the properties of the meta if they are available.
But as said in comments above, the meta defined in a component option is not available in $route.meta
. I think this is a real problem as it makes the feature kind of obscure and inconsistent.
For now I solved it by evaluating this.$nuxt.context.route.meta[0]
in a $route
watcher (to have it properly updated on navigation).
But I'm not a fan of this approach and I think I will fallback to using a slotted component instead of the nuxt layout. :/ That way I'll be able to pass props, which is way more straight forward if someone else comes behind and checks what is going on.
I wonder if you see a better strategy to solve my use case. @Atinux
Also this is linked to #7391, if not the same issue.
I'm also with @darkylmnx on the topic: I expected meta
to be available in the generated routes array (so I could see it on this.$route
). I also don't think it would need to be dynamic, but the page's meta could be merged into the routes sometime during the build, so the router would be aware of it (and consequently we could access it on this.$route
). I'd imagine instead of having the following in .nuxt/router.js
export const routerOptions = {
// ...
routes: [{
path: "/about",
component: _4a7bea44,
name: "about"
}, ...
]
// ...
}
we could have
export const routerOptions = {
// ...
routes: [{
path: "/about",
component: _4a7bea44,
name: "about",
meta: { ... } // <- meta copied in here
}, ...
]
// ...
}
My problem, too, is that the current behaviour feels inconsistent because without knowing the internals of Nuxt or without trying and failing at fetching a page's meta
in a page from the router, I cannot tell whether the page's meta
is truly accessible or not (eg. meta
can be seen by middleware, but $route.meta
is just {}
when rendered in a page). I would expect these two (the route's meta
in a middleware and this.$route.meta
in a page) to resolve to the same object.
Looking at #7391, it does seem to suggest the same thing (ie. make meta
available to the router by injecting it into the generated routes).
any update on this?
Each issue - requires own tool So, dynamic routing - is a tool for a quick scaffolding application, without worries about complexity.
But when you are starting to build up a more complex application, with complex routing and metadata inside of each route - I would prefer & propose to switch to the explicit routing with separate router config files.
Try this alternative:
function extractMeta(route) {
const routeRecord = route.matched.slice(-1)[0]
if (routeRecord) {
return routeRecord.components.default.sealedOptions.meta
}
}
Still no update here? Seems like a pretty big gap in consistency. I spent a number of hours beating my head on this issue thinking I was doing something wrong. Then I found this issue in GH. Thanks @caikan for the workaround! It definitely works but is surely extremely fragile due to its use of undocumented internals.
[Update] Actually sealedOptions doesn't work either. It is only present on initial page load in my SPA Nuxt, but client side route transitions return a diff object shape that does not include sealedOptions...
So back to the drawing board.