nuxt icon indicating copy to clipboard operation
nuxt copied to clipboard

Vue router construction should includes page's meta info

Open washanhanzi opened this issue 4 years ago • 10 comments

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.

washanhanzi avatar Apr 15 '20 06:04 washanhanzi

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  
})

Kunjinkao0 avatar May 13 '20 10:05 Kunjinkao0

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 avatar May 13 '20 14:05 Atinux

@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 or to.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

darkylmnx avatar May 22 '20 19:05 darkylmnx

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

sylann avatar Jun 16 '20 18:06 sylann

Also this is linked to #7391, if not the same issue.

sylann avatar Jun 18 '20 13:06 sylann

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).

Hanziness avatar Jul 09 '20 17:07 Hanziness

any update on this?

alijaya avatar Apr 03 '21 19:04 alijaya

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.

VRuzhentsov avatar Apr 06 '21 13:04 VRuzhentsov

Try this alternative:

function extractMeta(route) {
  const routeRecord = route.matched.slice(-1)[0]
  if (routeRecord) {
    return routeRecord.components.default.sealedOptions.meta
  }
}

caikan avatar May 07 '21 09:05 caikan

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.

mvandiest avatar Aug 27 '22 20:08 mvandiest