Nuxt.js: Payload undefined when navigating to page with nuxt-link

Created on 19 Nov 2017  路  18Comments  路  Source: nuxt/nuxt.js

In my nuxt.config.js, I have:

generate: {
        routes: function () {
            return axios.get('https://someapi.com/api/produkter').then(function (res) {
                const produkter = res.data.map(el => el.fields)
                return [{
                    route: '/produkter/anledninger/jul',
                    payload: produkter
                }]
            })
        }
},

In my page (pages/produkter/anledninger/_id.vue), I have:

async asyncData ({ params, error, payload }) {
            console.log('payload', payload)
            return {
                produkter: payload
            }
}

Generating static site with nuxt generate.
When I navigate to this page via a nuxt-link, payload is undefined. Upon refresh (or navigating via < a>), payload is populated with the data.

Using nuxt 1.0.0-rc11.

This question is available on Nuxt.js community (#c1904)

Most helpful comment

I am also having the same problem in nuxt 1.4.0.

All 18 comments

I am also having the same problem in nuxt 1.4.0.

Payload is only for nuxt generate, not for normal page navigation.

So nuxt-link is not supported for dynamic routes with payload and nuxt generate?
I am asking because the documentation for generate does not hint to this in any way or I cannot find it. And the generated HTML already contains all the content, so I don't see the benefit of any axios-fallback (in case of a REST-API) getting the data again for every request when using nuxt-link.

Is there any easy way to mimic it or do we have to re-implemented it using normal links? (things like transitions and loader)

asyncData doesn't run on the client at all on first load, payload doesn't affect that.
it only runs on the server.
In case of nuxt start it runs in real time.
In case of nuxt generate it runs before doing page render.

So, on your static page, you aren't seeing payload being equal to something other than undefined, you are just seeing the rendered result of it being defined on the server, leading to some data being loaded into the component.
This is why if you will see payload {/*...*/} in the node console while generating, payload undefined in browser console when using nuxt-link or router-link, and absolutely goddamn nothing if you load the page from url or <a>

payload is not a normal way to distribute data to your application. It is just an optimization to stop your nuxt generate from doing 9001 possibly redundant network calls when you do have the data available.
The benefit of nuxt-link is that only new data and new components need to be loaded to go to another page, not the whole html, and the libraries don't need to be initialized again. (Plus transitions and all that bourgeoise jazz)

If your data is static, you can provide it as json in your static folder.

See this repo for example of generating static data and serving it with both payload for faster generation and normally: https://github.com/qm3ster/linnertmedia/tree/1162ac3aed1f2d2995c49b7df844d1fdff5fa1c6

Sorry to ask some more stupid questions, but I am really confused now.

asyncData doesn't run on the client at all on first load, payload doesn't affect that.
it only runs on the server.

The asyncData-block does run on my client when I am using nuxt-link to go to the page (every single time). I can see the request to the REST-API in the browser (following the code in the documentation for payload AND using the static pages generated by nuxt generate).

absolutely goddamn nothing if you load the page from url or \

Actually the page is just fine when I load it from URL or using \

payload is not a normal way to distribute data to your application.

I would argue that it is a one of the available ways, at least I don't see it as something unusual when I read the documentation.
From the documentation:

In the example above, we're using the user.id from the server to generate the routes but tossing out the rest of the data. Typically, we need to fetch it again from inside the /users/_id.vue. [...] Because this will increase the run time of the generate script, it would be preferable to pass along the entire user object to the context in _id.vue.

To me this sounds like a legit usecase.

If your data is static, you can provide it as json in your static folder.

My data is coming from a headless CMS, so I can't just put it into a static folder.

Maybe the documentation is just missing information on the behaviour, but I find this very confusing at the moment.


So nuxt generate will generate dynamic routes with payloads as static HTML files with all data present, but these will not be used by the router (and nuxt-link) and only delivered when accessing their URLs directly. If we want to use nuxt-link to go to these pages, the pages have to get the data again from their source each and every time a user uses vue-routing.

Did I understand it correctly?
Isn't that "dangerous"? The pages accessed using nuxt-link could result in a different content than the generated static files, if the source changes and the static files have not been re-generated.

Isn't that "dangerous"? The pages accessed using nuxt-link could result in a different content than the generated static files, if the source changes and the static files have not been re-generated.

Again, it's dangerous if you are using payload in a weird way. If you are just using it to bypass a normal working asyncData(), there is no risk on your part.

I would argue that it is a one of the available ways, at least I don't see it as something unusual when I read the documentation.

Precisely wherefrom you're quoting the documentation, the section title is
Speeding up dynamic route generation with payload
This means exactly that, that it's just a way to make the generate of an otherwise working project take less time and resources.

  1. Pretend it doesn't exist. (That is why it's not simulated in dev, etc.)
  2. Create a normal working project that runs in nuxt dev and nuxt build+nuxt start
  3. Do a cost/benefit analysis of doing generate normally or the extra complexity of providing a payload.
  4. Act accordingly.

My data is coming from a headless CMS, so I can't just put it into a static folder.

If you want your project to not hit the CMS, you aren't providing it data from the CMS.
You are importing the data into the project at generatetime and then providing it from the project dist.
If you can fetch it at generatetime, you can also fetch it before generatetime and save it, by using your CMS's API as the datasource instead of the folder of .md files in the repo I linked.

Alternatively, you can just have it hit your CMS. This would mean that your pages would be in terrible tragic danger of getting more updated content when using <nuxt-link>.
With this approach, you could try SPA mode so that you don't have pregenerated content pages, then your content would always be fresh and you wouldn't have to redeploy to change it.

@Atinux is there a way to make some pages act like spa and some like normal generate?
For example generate all about/home/contact pages, but (if I redirect all requests to /user/* to user.html) have it behave like spa and not have stale information (but be accessible at initial navigation)?
Or is using query instead of route params and asynchronously loading the data into the already mounted page the only way right now?

@qm3ster thanks for the help so far. I really have to dig through the documentation some more I guess.
Maybe I just understand things differently (e.g. some confusion between static pages and ssr pages?).

E.g. "Speeding up dynamic route generation with payload"
I thought that meant faster generation of the static HTML which would then be used throughout nuxt. Might be helpful to add some use cases where it can help and where it can't.

No HTML gets used/shipped in nuxt except initial connection to server, whether that is static generate or live start.
The generate'd page truly does have the results of the asyncData, they are in fact included at the end of the html along with some other data.
But normal nuxt client navigation involves fetching just data, not the libraries and components you already have, and especially not html.
If you want your site to be stale/frozen, what problem do you have with the architecture I proposed (with the static jsons)?

This just looks like unnecessary overhead from my inexperienced (and as far as nuxt is concerned also uneducated) point of view.
I would need additional code to pull in all data as json / markdown / whatever, pre-compile as necessary and write it to the dist-directory. This adds more complexity to the project.

This also doesn't really fit into what nuxt wants to be (I think).
From https://nuxtjs.org/guide:

Its main scope is UI rendering while abstracting away the client/server distribution.

Not really, on the other hand we have even more complexity because of it (pre-compiling data as in your example or in other cases making sure both the client and server can access an API etc. and dealing with any inconsistencies).

Nuxt.js presets all the configuration needed to make your development of a Vue.js Application Server Rendered more enjoyable.

This is not really server rendered anymore is it? Sure in your example you are taking extra steps to pre-render the content on the server, but the page itself is still rendered on the client when using nuxt-link (we still have to wait for the data to be loaded in a separate request made by the client).
Depending on the users network speed and condition and the size + complexity of the data, the perceived speed might differ greatly between accessing the page using nuxt-link or going to the page by using the URL directly. We also have more requests hitting the server, things like image pre-loading probably also won't work and there is additional work for vue which has to parse the response and place the content.

But I have also seen the last paragraph, which mentions exactly the problem that we are talking about (but also does not explain why):

We can go further by thinking of an e-commerce web application made with nuxt generate and hosted on a CDN. Everytime a product is out of stock or back in stock, we regenerate the web app. But if the user navigates through the web app in the meantime, it will be up to date thanks to the API calls made to the e-commerce API. No need to have multiple instances of a server + a cache anymore!

At least for me, this does not really match with the other things described previously and should really be repeated as information and as an example for the _generate_ command page.

I would need additional code to pull in all data as json / markdown / whatever, pre-compile as necessary and write it to the dist-directory. This adds more complexity to the project.

No, you can do without that. You can always just make the network requests, and do as much processing as you want on the client. That way server and client depend only on exactly the same code, even in generate
Everything beyond that is just optimization shortcuts.

making sure both the client and server can access an API etc. and dealing with any inconsistencies).

Nuxt goes a long way towards dealing with those inconsistencies, and provides the tools to deal with the rest of them.

but the page itself is still rendered on the client

all generated pages, and all initial/nojs loads of the pages with a living server are completely server rendered. You can view them with javascript off and they will look exactly the same. (Unless you wrote some shameful jQuery stuff and are manipulating the dom directly)

The page is "rendered" on the client because we want it to be. We want it to have clientside data. We want it to be rendered without a server request at all on repeat navigation. We want access to other instances of a parameterized "dynamic" route to not download any structure, only data.

THE LAST THING

Yes, it describes the behavior you talked about. Glad to see that's documented.
What previous thing that you read does this conflict with?

I promise I am not trying to troll, but everything you told me so far makes it look like nuxt creates something that is more similar to "mixed" side rendered pages.
I now understand that on initial load any previously generated page will be delivered and once "in the app" it will be rendered on the client.

The page is "rendered" on the client because we want it to be. We want it to have clientside data. We want it to be rendered without a server request at all on repeat navigation. We want access to other instances of a parameterized "dynamic" route to not download any structure, only data.

Maybe that is the problem. I do not want it to be like that. I want it to get all the pre-rendered pages from the server. That is also the reason why it is overhead or a problem for me to make sure that the data is definitely coming from the very same server and is the same between the pre-generated any "dynamic" pages.

But if that is how nuxt currently operates and what has been defined for its operation and not just your opinion (I am not sure which based on your wording), then that is fine. I do think that the documentation should / could be improved to point out that fact (both in the introduction and the generate payload command section). For me, an example is not a good place to define facts but rather to further explain them. I am convinced that I would have never been this confused if the difference between the generated / server side rendered pages and the client-side navigation had its own section in the introduction which briefly talks about that. Then the example given at the bottom would actually make sense, as the behaviour behind that would have been explained.
A "Server side rendered vs. client-side navigation" section would be a real win for the documentation in my opinion (or would that actually be pre-generated vs client-side navigation? if I am finally on the right track here).

I am definitely not qualified to create a PR for that myself, but I will probably create an issue suggesting the addition of such a section. Maybe there is positive feedback for that and someone from the team or community can add that information.

Thanks for your help so far.

Thanks for the detailed notes from both of you @qm3ster and @msgeissler. I have to say this confused me too. Here is why.

I am statically generating a site which has pagination. I am passing through the payload to the archive pages e.g page/2 and the statically generated files are successfully created.

Lets say someone visits page/3 and gets a fairly old generated file. In the meantime new posts get added to the CMS messing with the pages and numbers. Now the visitor pages forward to page/4 and we make a nice fresh API request. Half of the posts they have just seen on the old page/3 are now visible on page/4. Paging backwards gets the stale archive of page/3 shown again as no fresh requests are made (or so it seems).

If nuxt continued to serve just the generateed files, this wouldn't be a problem, it would be up to me to regenerate with each new CMS post (easy enough to do via webhook). As it stands I will still need to do that, but loose the benefits of the static cache when paging around.

I also had the same issue and I have found a work around for this.
Created a plugin:

let fs = require('file-system');

export default {

    saveStaticApiData({fileName = 'test', fileContent = '{}'}) {
        fs.writeFile("./static-api-data/" + fileName + ".json", JSON.stringify(fileContent));
    },

    getStaticApiData({fileName = '{}'}) {
        let staticData = {};

        try {
            staticData = require("../static-api-data/" + fileName + ".json");
        } catch (ex) {}

        return staticData;
    }

};

And you can use this plugin in the nuxt pages asyncData:

import ApiManager from '../../plugins/api-manager'

export default {
    async asyncData() {

            if (process.server) {

                // dynamic data
                let res = await axios.get("api-url");

                let asyncDataCollection = res.data;

                ApiManager.saveStaticApiData({fileName: "staticApi", fileContent: asyncDataCollection})

                return asyncDataCollection;

            } else {

                return ApiManager.getStaticApiData({fileName: 'staticApi'});

            }

        }
}

And you have to call the nuxt generate twice.

Please let me know your suggestions.

I came up with a similar solution. I hook into the generate function and cache the API responses locally:

https://github.com/scottsweb/scott.ee/blob/master/modules/generate.js

Then in production I modify the axios URLs to point at the locally cached JSON:

https://github.com/scottsweb/scott.ee/blob/master/plugins/axios.js#L1-L28

This question has been resolved by @scottsweb, see answer.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vadimsg picture vadimsg  路  3Comments

VincentLoy picture VincentLoy  路  3Comments

vadimsg picture vadimsg  路  3Comments

mikekidder picture mikekidder  路  3Comments

lazycrazy picture lazycrazy  路  3Comments