How to return Vuex-generated page to client on initial Vue load? - vue.js

I have a Vue / Nuxtjs app which displays lots of user-provided content (think of it as a crowdsourced blog). The content on the client is retrieved and stored in Vuex. When a page is loaded, it displays the current content and then uses fetch to get the updated data. Here is a typical component:
fetch() {
this.$store.dispatch('feeds/refreshLatest')
},
computed: {
feed() {
return this.$store.state.feeds.latest
}
}
where feeds/refreshLatest uses axios to retrieve the posts.
This works quite well. The problem is the initial load is very slow, especially on the front page which has to process and display dozens of articles.
I have SSR enabled, and would like the server to store the content, and then on initial load provide a rendered page to the client. However, the Vuex object on the server seems to be new for each request, and so the client has to wait for the entire set of articles to be fetched before anything is displayed, which is unacceptable. Doing all the fetches only on the client solves this problem, but it is still too slow.
I thought I could somehow use the same server Vuex on each call and sending it to the client with nuxtServerInit, but I don't see a way to achieve sharing the Vuex. Thank you for any pointers or other packages which could help.

The question is that after the fetch is finished after the api call in the server rendering, the DOM is dropped to the client, and the process is running every time and slow?
I solved similar issues using cookies. This is because cookies can also be used to render servers. I used the method below.
Store the data in the cookie after the initial api call, and send the data in the cookie to the client first.(If cookies are present, do not call api from server)
Call api from client to update data.
I use this library.
https://github.com/microcipcip/cookie-universal/tree/master/packages/cookie-universal-nuxt#readme

Related

Fetch data after an update

VueJS + Quasar + Pinia + Axios
Single page application
I have an entity called user with 4 endpoints associated:
GET /users
POST /user
PUT /user/{id}
DELETE /user/{id}
When I load my page I call the GET and I save the response slice of users inside a store (userStore)
Post and Put returns the created/updated user in the body of the response
Is it a good practice to manually update the slice of users in the store after calling one of these endpoints, or is better to call the GET immediatly after ?
If you own the API or can be sure about the behavior of what PUT/POST methods return, you can use local state manipulation. Those endpoints should return the same value as what the GET endpoint returns inside. Otherwise, you might end up with incomplete or wrong data on the local state.
By mutating the state locally without making an extra GET request, the user can immediately see the change in the browser. It will also be kinder to your server and the user's data usage.
However, if creating the resource(user, in this case) was a really common operation accessible by lots of users, then calling the GET endpoint to return a slice would be better since it would have more chance to include the new ones that are created by other users. But, in that case, listening to real-time events(i.e. using WebSockets) would be even better to ensure everyone gets accurate and new data in real-time.

Updates using server-side rendering without page refresh

I was reading an interesting blog. Here the author said:
Updates using server-side rendering is where a lot of developers start
going off the deep end. They actually think page refresh. Instead,
what I thought we've all been doing for the last half decade, is some
form of:
$('#loadTweets').on('click', function(e) {
$.get('/tweets/person', {last_id: 239393939}, function(r) {
$('#tweets').prepend(r);
});
e.preventDefaults();
});
In other words, we are still only doing a partial update, but letting
the server do the rendering and inserting that finalized output into
our DOM.
I did not understand what he meant by "is some form of:...we are still only doing a partial update".
I mean, if I understood correctly, server sending the html and css on every request is Server-Side Rendering (SSR). Server sending json on every request except first is Client-Side Rendering (CSR).
As far I understand, in the code bellow, if r is json then it is CSR and if r is html then it is SSR:
$.get('/tweets/person', {last_id: 239393939}, function(r) {
$('#tweets').prepend(r);
});
What am I getting wrong here?
Bassed on your definition of SSR vs CSR
Server sending [HTML] on every request is Server-Side Rendering (SSR)
Server sending [JSON] on every request except first is Client-Side Rendering (CSR).
let's try to apply that to the example logically:
$.get('/tweets/person', {last_id: 239393939}, function(r) {
// do stuff with `r`
});
For that I made your statements into this decision table. (I'll get to the undefined cases right away, keep reading.)
First Response?
JSON
HTML
Yes
undefined
SSR
No
CSR
undefined
First, we check whether it's the first request. We can say without problems that it is not, wouldn't the client have gotten the JavaScript earlier it couldn't be running it.
Now let's introduce what type of data is send:
if [response] is json then it is CSR
if [response] is html then it is SSR
The first statement is valid, it would definitely be CSR. But the second one would lead an undefined case. We're deeply confused now!
To address that, let's read how the author defines as CSR and SSR:
With client-side rendering, your initial request loads the page layout, CSS and JavaScript. It's all common except that some or all of the content isn't included. Instead, the JavaScript makes another request, gets a response (likely in JSON), and generates the appropriate HTML (likely using a templating library).
With server-side rendering, your initial request loads the page, layout, CSS, JavaScript and content.
For now, this leads to a similar table than yours, but note how the format/type headers are slightly different!
First Response?
Data as JSON
Data as HTML
Yes
undefined
SSR
No
CSR
undefined
S/He continues:
For subsequent updates to the page, the client-side rendering approach repeats the steps it used to get the initial content. Namely, JavaScript is used to get some JSON data and templating is used to create the HTML.
So s/he's now at the second row of his table, i.e. not the first response.
Then your quote starts (emphasis mine):
Updates using server-side rendering is where a lot of developers start going off the deep end. They actually think page refresh. Instead, what I thought we've all been doing for the last half decade, is some form of [...] doing a partial update, [...] letting the server do the rendering [of the HTML] and inserting that finalized output into our DOM.
With this we can fix the undefined case in the not-first-request-row we have been confused about!
First Response?
JSON
HTML
Yes
undefined
SSR
No
CSR
Partial Update
There is still the first-response-JSON-case, but as the browser cannot generate further requests from this on it's own we can ignore it here.
Hope this helps!

How do I update my data based on the query parameter in vue-router

I am new to vue-router and I am facing this problem.
Here is my code;
filter() {
this.$router.push({
query: {
bathrooms: this.selectedBathroom,
beds: this.selectedBed,
minPrice: this.selectedMinPrice,
maxPrice: this.selectedMaxPrice,
minLandSize: this.selectedMinLandSize,
maxLandSize: this.selectedMaxLandSize,
minLotSize: this.selectedMinLotSize,
maxLotSize: this.selectedMaxLotSize,
minYear: this.selectedMinYear,
maxYear: this.selectedMaxYear,
},
})
}
when user clicks on the filter button, the filter method is run. But the data doesn't update and when the user goes back to the previous page, the the query params is removed, but the data isn't updated.
Is they a way I can work around with this?
There is no path or name value here to actually initiate a change to the page route. If you want to simply reload the existing page with a new set of query parameters see this information from the documentation:
Note: If the destination is the same as the current route and only params are changing (e.g. going from one profile to another /users/1 -> /users/2), you will have to use beforeRouteUpdate to react to changes (e.g. fetching the user information).
In your comments you mentioned that you are making a request to a server. It's hard to give a full answer without more code from your application but query parameters might not be the most straightforward way to approach your problem. You should explore setting up a fetch request in your method or using axios to interact with the server (assuming you're communicating via an API) to build a more reliable and better scaling communication pattern.

Prefetching API Data with Apollo for Nuxt.js SSR

I've got what is hopefully a very simple question about prefetching data from within an apollo/nuxt configuration.
I am requesting data from a GraphQL API and want to prefetch the data to use server-side rendering. Reading the docs it seems like I should simply be able to set prefetch: true on my apollo query but this is not working – it is always sending the request from the client side which is causing all kinds of issues.
Here is the code in my component:
apollo: {
concept: {
prefetch: true,
query: conceptStatements,
variables () {
return { id: this.$route.params.id }
}
}
}
I feel like it will have something to do with when this.$route.params.id is evaluated?
Ultimately I would like to create a Vuex store in the store/index.js which takes all of my apollo queries and renders the data in them accessible across the application but the documentation is very vague on how I might do this. Any help would be much appreciated!
Are you really sure your data is not prefetched?
Prefetch is for the first rendering (in SSR mode) ; Nuxt/Apollo call your graphql api, generate the page and send it to the browser. If you just navigate to your page from another route, nuxt call your api from the browser.
You can check this behavior in devtools/network, select xhr. Refresh your page, there is not xhr call. Go to another route and refresh your page, then navigate to your route where is your apollo query and you should see a xhr call to your graphql api.
Also, you can configure this behavior in your apollo config with fetchPolicy.
You don't need Vuex to store response of your apollo queries:
From vue-apollo doc:
When you perform GraphQL queries with Apollo, the results of API calls
will be stored in Apollo cache. Now imagine you also need to store
some kind of a local application state and make it available for
different components. Usually, in Vue application we can achieve this
with Vuex. But having both Apollo and Vuex will mean you store your
data in two different places so you have two sources of truth.
So, if your perform same query from different page or component, Apollo don't call your api each time, but retrieve data from the Apollo cache. Apollo is just magic!

Relay modern caching example

I would like to enable caching in my react native application. I am using GraphQL with Relay modern. I found out that caching is not enabled by default in relay modern, but they have exposed RelayQueryResponseCache from relay-runtime, which we can add to the fetchQuery function in our API. I read discussion here and here about it, but have not seen any example to get started. Can someone help me out on this?
EDIT:
Ok I came up with a solution. I think it misses few things but so far it serves our needs.
I have noticed that passing anything into QueryRenderer into cacheConfig results passing that value into fetchQuery function inside my environment.
So I have created a Component which loads the data by some relation and resolves it into the correct json structure requested by the query. Then I return this into the state. Then I extended my Component which contains QueryRenderer with the created 'cache loader'. Now when componentWillMount() is called I ask for the cached data. During this I have set this.state.loading = true so I am able to handle loading state. Reading from DB is async.
I am using this class in other components as well. Every one handles its cache data. I just pass it to QueryRenderer.
However I was thinking that this makes some extra logic need to add for each Component which is supported by this caching. Probably passing the cache resolver as cacheConfig and resolve the cached data immediately inside the environment would be much more cleaner.