GithubHelp home page GithubHelp logo

Comments (38)

userquin avatar userquin commented on August 19, 2024 8

@husayt just clone this repo and change the lines 91 and 120: on former remove the inicial state and later add the initial state, on build you will have on each html page its own state, just modify the router example with your logic

from vite-ssg.

husayt avatar husayt commented on August 19, 2024 2

@userquin if this issue is fixed I expect the code above to just work. That would make vite-SSG so much more powerful and flexible

from vite-ssg.

garySiladi avatar garySiladi commented on August 19, 2024 1

I now experience the same issue.

from vite-ssg.

pantajoe avatar pantajoe commented on August 19, 2024 1

You are right, I would say we need both for vite-ssg to be most flexible. The callback you propose can initialize the state and we also need to provide a configurable way to allow for custom serialization/deserialization of all states (global and page isolated states), and this is already provided by the transformState callback. So, maybe a callback like initializeGlobalState in the ViteSSG function might be helpful.

Concerning the page state, it believe it's best to provide a composable like useAsyncData or usePageState maybe.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024 1

Something similar to this:

export const createApp = ViteSSG(
  App,
  { routes },
  // NEW GLOBAL STATE CALLBACK
  (/* ARGS NOT AVAILABLE YET*/) => {
    // here global state will be loaded
  },
  ({ app, router, routes, isClient, initialState, routePath, globalState }) => {
    ...
    if (isClient) {
      // restore page state
      // restore global state
    } else {
      if (import.meta.env.VITE_SSG) {
        // here the `initialState` will be always the page state, the global state will be handled from  NEW GLOBAL STATE CALLBACK
        initialState.data = { name: routePath ==="/a" ? ''ALPHA" : "BETA" }
        console.log('saving ' ,initialState.data)
      } else {
        // here store the global state: `globalState` will be populated by NEW GLOBAL STATE CALLBACK
      }
    }
    ...
  }
)

from vite-ssg.

husayt avatar husayt commented on August 19, 2024 1

@userquin do we need import.meta.env.VITE_SSG since we already have isClient?
i thought that import.meta.env.VITE_SSG === !isClient

from vite-ssg.

userquin avatar userquin commented on August 19, 2024 1

If you use mock: true option on ssgOptions: see https://github.com/antfu/vite-ssg/blob/main/src/node/build.ts#L106, the window is defined...

I've never get it working (JSDOM), it always throws an error, maybe we need to add some thing... see

from vite-ssg.

husayt avatar husayt commented on August 19, 2024 1

@userquin not sure about page or global scoped state, but here is the bug originating from how the static pages generated during SSG. On one side to generate the body of html plugin uses page specific state, but it adds the same

<script>window.__INITIAL_STATE__={data:{...}</script>

to the footer of all of these pages

it seems it generates initial html first

[vite-ssg] Build for server...
vite v2.5.6 building SSR bundle for production...
saving { name:"ALPHA" } 

and then when rendering pages it uses bits from the html generated during initial render

[vite-ssg] Rendering Pages... (378)
saving { name:"ALPHA" } 
saving { name:"BETA" }

from vite-ssg.

userquin avatar userquin commented on August 19, 2024 1

@husayt The console log is just a trace, the state saved is not that state, if you see this line https://github.com/antfu/vite-ssg/blob/main/src/node/build.ts#L133, the state is saved here but using the initial state outside the promise loop defined here https://github.com/antfu/vite-ssg/blob/main/src/node/build.ts#L91 instead using the state of each page, that should be declared here https://github.com/antfu/vite-ssg/blob/main/src/node/build.ts#L120.

With my changes, your initial state will have the scoped page state, that's, each page with its own state. Just see the /a.html state on my screenshot.

The state you've on the console is a not the state that will be saved, since the L133 is using the reference on L91 and not using the state on L120, that it is the state your are showing on console.

The initialState on L91 is the global state while the state that should be retrieved from L120 is the page scoped.

from vite-ssg.

husayt avatar husayt commented on August 19, 2024 1

@userquin one more thing, as I expected , router hook is not required. It just works

from vite-ssg.

yassilah avatar yassilah commented on August 19, 2024

It should indeed be fixed by #93, sorry for the duplicate issue @antfu

from vite-ssg.

husayt avatar husayt commented on August 19, 2024

@yassilah No, unfortunately, this is still not resolved.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@husayt seems there is a problem, here some inputs, I'll try to fix it tmr or later, I'm very busy at work.

/cc @antfu : maybe can help to digging into the problem.

I'm testing with your changes on multiple-pages-with-store example:

export const createApp = ViteSSG(
  App,
  { routes },
  ({ app, router, initialState, routePath }) => {
    const pinia = createPinia()
    app.use(pinia)

    if (import.meta.env.SSR) {
      console.log(`RoutePath: ${routePath}`)
      console.log(`SSR Pinia state: ${JSON.stringify(pinia.state.value)}`)
      // this will be stringified and set to window.__INITIAL_STATE__
      initialState.pinia = { routePath: routePath || '/' } // pinia.state.value
    }
    else {
      console.log(`Pinia state: ${JSON.stringify(initialState?.pinia || {})}`)
      // on the client side, we restore the state
      pinia.state.value = initialState?.pinia || {}
    }

    router.beforeEach((to, from, next) => {
      const store = useRootStore(pinia)

      store.initialize()
      next()
    })
  },
  {
    transformState(state) {
      return import.meta.env.SSR ? devalue(state) : state
    },
  },
)

Seems there is something wrong with the app state, you can see on following screenshot at bottom rendering the /nested/deep/b, I think some missing await on the logic:

The html part on SSG rendering is this for /nested/deep/b page:

<html>
....
<body>
  <div id="app" data-server-rendered="true"><main data-v-9aed9e50=""><div class="markdown-body" data-v-9aed9e50=""><h1>Page A</h1></div></main></div>
<script>window.__INITIAL_STATE__={pinia:{routePath:"\u002F"}}</script>
</body></html>

imagen

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@antfu please fix the problem, I cannot make a PR rigth now.

I've found the problem, we are using the SSR initialState instead each page ones':

const { routes, initialState } = await createApp(false)

then inside the Promise.all block we're using this initial state instead the createApp one (lines 88 and 114):

imagen

imagen

imagen

from vite-ssg.

husayt avatar husayt commented on August 19, 2024

@userquin that might explain what is going on there. One other benefit of this fix, would be allowing for much easier store initialisation. One would not need router.beforeEach now. It becomes so much simpler

export const createApp = ViteSSG(
  App,
  { routes }, ({
  router,
  app,
  head,
  isClient,
  routes,
  initialState,
  routePath,
}) => {

  const pinia = createPinia()
  pinia.use(({ store }) => {
    store.router = markRaw(router)
  })

  app.use(pinia)

  let trans = []
  if (!isClient) {
    // this will be stringified and set to window.__INITIAL_STATE__
    initialState.trans = getRouteSpecificState(routePath)
  } else {
    // on the client side, we restore the state
    trans = initialState.trans
  }

  const strg = useStorageStore(pinia)
  strg.initialise(trans)
}))  

@antfu this is why it is so important to get this right. I tried to fix it few times myself, but was really confused.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@userquin that might explain what is going on there. One other benefit of this fix, would be allowing for much easier store initialisation. One would not need router.beforeEach now. It becomes so much simpler

export const createApp = ViteSSG(
  App,
  { routes }, ({
  router,
  app,
  head,
  isClient,
  routes,
  initialState,
  routePath,
}) => {

  const pinia = createPinia()
  pinia.use(({ store }) => {
    store.router = markRaw(router)
  })

  app.use(pinia)

  let trans = []
  if (!isClient) {
    // this will be stringified and set to window.__INITIAL_STATE__
    initialState.trans = getRouteSpecificState(routePath)
  } else {
    // on the client side, we restore the state
    trans = initialState.trans
  }

  const strg = useStorageStore(pinia)
  strg.initialise(trans)
}))  

The problem is that we are using an SPA and so the initial context state will be only applied if using the router, you are not rerendring the html page, only when refrshing page. The initialState is global, cannot have one store for each page...

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@husayt maybe I'm wrong ;)

from vite-ssg.

pantajoe avatar pantajoe commented on August 19, 2024

@husayt So what's the desired outcome of the code you provided?
I'm not quite sure if I understood it correctly.

So, do you wish for the initial state to be different depending on which route is accessed first when accessing the page?
Example:

  • you access the page /index, your state is { routePath: '/index' }. Then as you use vue router to travel to different pages, the initial state remains { routePath: '/index' }.
  • as soon as you access another page for the first time or when reloading a page, e.g., /another-page, then your state is at first { routePath: '/another-page' } and it remains as you travel through the page with vue router (SPA like navigation)

Or do you wish for the initial state to be different depending on which page is currently accessed (including navigation via vue router)?

  • you access the page /index, then your initial state is { routePath: '/index' }. As you travel through the page with vue-router, your initial state changes
  • it would also change to the path of any page you are currently visiting. Both by using vue-router or reloading the page.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@pantajoe The problem here is that we have an SPA, not an MPA, I'm really confusing about the behavior @husayt wants: remove the initial state from the router on page transition will prevent the behavior, unless the state is shared between pages and just added on each route, but then, my suggestion will break that, since the initialState is created on each route.

If we have an isolated initialSate for each page, then it will need to be loaded by the router (like vuex scoped/namespaced modules where the module is shared between groups of pages and required to be loaded with the router hook without this: beforeRouteEnter)

from vite-ssg.

pantajoe avatar pantajoe commented on August 19, 2024

Exactly, that's why I'm confused, too. I implemented this initialState feature because I needed something to be run once during SSG that's available in all pages, similar to the hook nuxtServerInit when you are using Nuxt, i.e., one API to provide available languages, configuration, whatsoever, to the app and that's properly hydrated during static site generation.
Hence, I'm not sure if it makes sense to modify the initial state in such a way that @husayt can executed the provided code as intended.

The question here is what does getRouteSpecificState do? Because I feel like this is another type of state, not the initialState, but the state of the page, a bit similar to asyncData in Nuxt.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

Yeah, on nuxt the vuex namespaced modules equivalent is asyncData (I don't know if it is using the beforeRouteEnter hook but seems that should use it on each route or some internal hook on router).

The problems here are:

  1. we need a global initial state?
  2. we need an isolated page state?

If we need a global state, we'll need to do 2 loops instead one (on src/node/build.ts, former to collect this global state, and later to generate the pages with this global state and page one, and should change the api to include both states.

If the global state is not required, then my suggestion will work, but the router will need to restore the state between page transition, with or without global state since this is not a MPA.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

Maybe the first loop is not required since it is global, but ppl need to know if calling for global state to just initialize the global only on / route (process.env.SSG will be true and process.env.VITE_SSG will be false).

I think we can add a callback to initialize this global state instead doing it on the app logic, it can be confusing.

@husayt what's exactly what you want? I think adding this 2 states will cover all cases.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

/cc @antfu sorry to bother you man, what do you think of this new proposal (read last 5 comments)?

from vite-ssg.

husayt avatar husayt commented on August 19, 2024

Let me address the issue from different end, to make it maybe more clear.
Let's say i have two pages a.vue and b.vue

// src/main.ts

// ...

export const createApp = ViteSSG(
  App,
  { routes },
  ({ app, router, routes, isClient, initialState, routePath }) => {
    // ...

    if (import.meta.env.SSR) {
      initialState.data = { name: routePath ==="/a" ? ''ALPHA" : "BETA" }
      console.log('saving ' ,initialState.data)
    } else {
    
      console.log(initialState.data) // => { name:"ALPHA" } or { name:"BETA" }
    }

    // ...
  }
)

what i expect is two files:

  • a.html to contain this
window.__INITIAL_STATE__={name: "ALPHA"}
  • and b.html to contain this
window.__INITIAL_STATE__={name: "BETA"}

The funny thing when i run build I see in console:

saving { name:"ALPHA" } 
saving { name:"BETA" }

but when both htmls generated, both contain the same:

window.__INITIAL_STATE__={name: "ALPHA"}

this is clearly wrong and here is the issue originates from.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@husayt so you need a page scoped state, this will be addressed with my proposed changes, but you will need to load the page state between page transitions, since the state is not global and so each page state will need to be restored.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

To handle also the global state, it requires to be included inside the import.meta.env.VITE_SSG if statement, since the SSR will be only used to load routes to traverse them on SSG.

Maybe we can also include isSsg and/or isSsr on the context instead force ppl use process.env or import.meta.env stuff.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

I'm writing some ideas here not in project source code, we need to review it.

isClient is initialized always with const isClient = typeof window !== 'undefined', see:

While process.env.SSR and process.env.VITE_SSG are initialized before index.html build and entry.ts build: on client side these 2 guys will be false and only true when building.

from vite-ssg.

husayt avatar husayt commented on August 19, 2024

@userquin Would there be a case when they are not the same ? i,e the following not true

import.meta.env.VITE_SSG === !isClient

from vite-ssg.

husayt avatar husayt commented on August 19, 2024

@userquin thank you for detailed explanation.
Seems really interesting. Where can I test your proposal? is there a pull request?

from vite-ssg.

husayt avatar husayt commented on August 19, 2024

@userquin this is exactly what was missing. Shall we do a pull request for that. This is the most useful one liner change ever.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@husayt We are waiting for antfu input, you can do a PR but I think he is working on this. Maybe the global state can be also added merging the global with the page on a single store...

The important thing is that the router hook will be required to switch this inital state, I hope you finally undertood why I was confused with your comments about removing the router hook ;)

from vite-ssg.

husayt avatar husayt commented on August 19, 2024

This is such a complex topic, as we have to understand behavior of the same code and its derivates on different sides, I think very few have a clear idea about what is going on.

Lets wait for what @antfu comes with. But your changes at least fix the current issue, so I use them on my website now

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@husayt I'm trying to figure out how is that posible, I'm 99% sure that shouldn't work, on page transition without the hook the New page should have the previous page state: and if you do a hard refresh should work.

I mean without this hook https://github.com/antfu/vite-ssg/blob/main/examples/multiple-pages-with-store/src/main.ts#L24

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@pantajoe this comment is the problem, so the code seems to be ok, the state is global not per page: https://github.com/antfu/vite-ssg/blob/main/src/client/index.ts#L70

@husayt this issue is a New feat, if previous comment is correct.

from vite-ssg.

pantajoe avatar pantajoe commented on August 19, 2024

@userquin @husayt I just think that this has to be revised in its core as we discussed earlier in this thread.

We need a global initial state like the state initiated in nuxtServerInit in Nuxt.js

We also need a state per page similar to asyncData in Nuxt.js

Maybe it might be a good idea to use the same approach for both states.

@antfu Sorry to bother you, but I think we need some input on this 😅

from vite-ssg.

pantajoe avatar pantajoe commented on August 19, 2024

Otherwise I will try to modify my open PR #67 as I proposed. In the meantime, I don't see no harm if @husayt's PR is merged for now

from vite-ssg.

husayt avatar husayt commented on August 19, 2024

@pantajoe you are raising very important topic here, but the pull request I have is fixing what is clearly a bug and is better to be merged separately to allow more constructive discussion here.

from vite-ssg.

userquin avatar userquin commented on August 19, 2024

@userquin @husayt I just think that this has to be revised in its core as we discussed earlier in this thread.

We need a global initial state like the state initiated in nuxtServerInit in Nuxt.js

We also need a state per page similar to asyncData in Nuxt.js

Maybe it might be a good idea to use the same approach for both states.

@antfu Sorry to bother you, but I think we need some input on this 😅

You can include a global state on your main.ts and then do a merge with the page state (the state passed on the cb is now the page state and not the global) or just copying it via Object.assign({}, globalState)}. If you have also page state, just add it to previous assignment.

You can also do the equivalent nuxt asyncData for page state since the callback can be also async:
fn?: (context: ViteSSGContext<true>) => Promise<void> | void,

from vite-ssg.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.