Comments (20)
I think both -- some people may not like the "black magic" - If anything, at least include a link to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description
I wasn't aware of it myself, actually, but guessed it's something like that based off of your example and found the documentation later
from vite-ssr.
@moeriki Not sure if you saw this one: initialState.urql = { toJSON: () => ssrCache.extractData() }
. This doesn't modify urql exchange either and seems simpler.
from vite-ssr.
Ah good to know! I removed devalue
for now. Thanks for all the feedback π
from vite-ssr.
@wucdbm Hey thanks. What's your use case for this exactly?
There's a transformState
hook that runs at the very end and gets the filled initialState
as its first argument. This is normally used to make arbitrary modifications to the initial state and it can also be asynchronous. Therefore, I think this can be used in most situations.
Would that work for you?
from vite-ssr.
I'll have a look next monday, that could very well be something that I overlooked!
from vite-ssr.
@wucdbm did it work?
from vite-ssr.
@frandiox Priorities shifted and it was left behind, I'll get back to the thread as soon as I'm back to that project
It's literally the only thing left before we have a stable project bootstrap and we must evaluate it against a custom solution
from vite-ssr.
@frandiox No, unfortunately, that hook will not help, let me attemp to describe the issue
My main.ts looks like this
export default viteSSR(
App,
options,
({ app, router, initialState, axiosBaseUrl, axiosHost, request }: HookParams) => {"
// some initialization code
// ...
const ssrCache = ssrExchange({
isClient: !import.meta.env.SSR,
// on the front-end, the client's cache is hydrated with the awaited state we previously set to initialState._urql,
// so it won't do any ajax queries once the ssr-rendered content lands -- components will fetch their data via
// this cache and avoid the network -- this also prevents loaders while network
// requests are running from spinning when we have the content from SSR
initialState: !import.meta.env.SSR ? initialState._urql : undefined,,
staleWhileRevalidate: true,
})
app.use(urql, {
url: 'https://rickandmortyapi.com/graphql',
exchanges: [
dedupExchange,
cacheExchange,
ssrCache,
fetchExchange,
],
})
The issue here is, that in my components, I do this
<script lang="ts">
import { defineComponent, onServerPrefetch } from 'vue'
import { getCharacters } from '@/gql/queries'
import { useQuery } from '@urql/vue'
export default defineComponent({
name: 'HelloWorldComp',
setup() {
const result = useQuery({ query: getCharacters })
onServerPrefetch(async () => {
await result
})
return {
fetching: result.fetching,
data: result.data,
error: result.error,
}
},
})
</script>
The thing is, I'll have the data in the ssrCache
I passed to urql earlier ONLY AFTER I have awaited all of its promises via onServerPrefetch
.
So in that sense, I need to be able to, in addition to head
, return a hook that will be ran by entry-server just after renderToString(app, context).then(deferred.resolve).catch(deferred.reject)
Then I am guaranteed that all of my promises will have been awaited, and then I still have access and can modify initialState
, like so:
if (ssrCache) {
initialState._urql = ssrCache.extractData()
}
(The reason I am not replacing initialState entirely is because I have other custom stores that hold other bits of data, these are simply fields in initialData and allows for future flexibility)
Essentially I am looking forward to a change such as
const { head, postRenderHook } =
(hook &&
renderToString(app, context).then(deferred.resolve).catch(deferred.reject)
const body = await deferred.promise
postRenderHook && postRenderHook() // << this should do the magic trick if the user of the library has exported this postRenderHook from their main.ts hook
PS I just modified my code exporting a hook like above, and modified the library's vendor files to add postRenderHook && postRenderHook()
as per above, and it works flawlessly, my data is now attached to the document as it should!
from vite-ssr.
@wucdbm Yeah I think I understand the use case. I've done this with Apollo before so I think it should work the same way with URQL.
I think this can work with transformState
without adding any more hooks, since transformState
is basically for doing this, transforming state after the app is rendered in SSR. Can you test something like this?
export default viteSSR(
App,
{
routes,
transformState(state, defaultTransformer) {
if (import.meta.env.SSR) {
// Make cache serializable in SSR
state.urqlCache = state.urqlCache.extractData()
}
// Continue default serialization/deserializaion
return defaultTransformer(state)
},
},
({ app, initialState }) => {
// Main initialization hook
initialState.urqlCache = ssrExchange({
isClient: !import.meta.env.SSR,
initialState: !import.meta.env.SSR ? initialState.urqlCache : undefined,
staleWhileRevalidate: true,
})
app.use(urql, {
url: 'https://rickandmortyapi.com/graphql',
exchanges: [
dedupExchange,
cacheExchange,
initialState.urqlCache,
fetchExchange,
],
})
}
)
The key here is to save the ssrCache in the initial state object so you have access to it later in the transformState
to serialize it.
There are other examples that follow this strategy with apollo and vue-query.
Please tell me if something like that works for you.
from vite-ssr.
@frandiox That'd definitely work, yes, but looks ugly -- IMO we shouldn't mess with business objects attached to a container that contains pure state only
On one hand, with my solution it'd need some more documentation or people to browse the source code to figure things out (I've done a lot of that on this and other libraries), on the other though, as a project evolves and becomes more complex, this hacking around may end up a huge shotgun wound in your foot
It's your call ultimately, but I'd keep the state object and other containers that implement business logic completely separate
from vite-ssr.
@frandiox Just to confirm, your solution works fine
from vite-ssr.
The problem in returning that hook is that it's not isometric because it would not be used in the browser.
It would be nice if the cache instance implemented a .toJSON
method that is automatically called by JSON.stringify
π€
Regardless, I'll give it a thought anyway since it's always possible to improve the API to something that is easier to understand.
Closing this for now since the main issues is solved π
from vite-ssr.
@wucdbm One more thing, can you try something like this?
({ app, initialState, isClient }) => {
// Main initialization hook
const ssrCache = ssrExchange({
isClient,
staleWhileRevalidate: true,
initialState: initialState._urql, // undefined in SSR
})
if (import.meta.env.SSR) {
ssrCache.toJSON = () => ssrCache.extractData() // or ssrCache.toJSON = ssrCache.extractData , not sure
initialState._urql = ssrCache
// edit -- I think this should also work:
// initialState._urql = { toJSON: () => ssrCache.extractData() }
}
app.use(urql, {
url: 'https://rickandmortyapi.com/graphql',
exchanges: [
dedupExchange,
cacheExchange,
ssrCache,
fetchExchange,
],
})
}
Not sure if this is easier but at least it doesn't need to define transformState
π€
from vite-ssr.
Yup, the one in the comment with toJSON
is actually great
PS Very cool, thank you very much!
from vite-ssr.
@wucdbm Do you think that would be a better fit for official examples? π€ (instead of using transformState hook)
from vite-ssr.
Another way.
Object.defineProperty(initialState, 'urql', {
enumerable: true,
get() {
return ssrCache.extractData();
},
});
I prefer it slightly over the other solution for it doesn't modify the urql exchange.
from vite-ssr.
Ah yes indeed a very good solution as well.
Unfortunately this doesn't seem to work when using devalue
for serialisation.
Found this issue. Rich-Harris/devalue#15
from vite-ssr.
@moeriki Ah right, this is only for JSON states. I think devalue outputs a JS object or function instead.
Out of curiosity, why do you need devalue? Are you serializing Map/Set, regexps or something complex?
from vite-ssr.
Maybe no good reason at all. I figured there were security concerns for JSON.stringify
.
I'm POCing a stack for an upcoming project. Only small test POJO payloads.
from vite-ssr.
@moeriki Ah I see. Vite SSR is applying some security measures though.
from vite-ssr.
Related Issues (20)
- Cannot read property 'ssrUtils' of undefined HOT 1
- Add support for streaming
- vueuse/head version
- δΈι’ζ―ζηζ₯ιδΏ‘ζ― δΉεθΏθ½η¨η°ε¨ε°±δΈθ‘δΊ HOT 6
- vite-ssr doesn't appear to be written in CJS, but also doesn't appear to be a valid ES module (i.e. it doesn't have "type": "module" or an .mjs extension for the entry point). Please contact the package author to fix. HOT 1
- vite-ssr build runs in development mode HOT 2
- vire-ssr has unmet peer dependency warnings
- Internal server error: render is not a function HOT 1
- Error isFunction is not a function from defineComponent called from ClientOnly component. HOT 2
- Unable to request data to set header title in hook function 'onServerPrefetch', page has already completed rendering before await HOT 1
- @vueuse/head 1.x versions Rendering problem
- What should I do if the client repeatedly renders after SSR?
- How to get request headers in Vercel HOT 1
- Bug: bad SSR index.html rendering. HOT 1
- Vue on client don't start lifecycle. HOT 1
- Failed to load url __vite-optional-peer-dep:@vueuse/head:vite-ssr HOT 1
- name export commafy not found
- Does this repo support code splitting?
- Vite 5 support HOT 3
- __DEV__ Vite config define breaks @elastic/apm-rum-core.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from vite-ssr.