telenko / node-mf-example

14 stars 3 forks source link

Dicussinon: CSS loading #3

Open fatawesome opened 3 years ago

fatawesome commented 3 years ago

Hello again :D

I am not an expert in nextjs, and I don't quite get how it avoids FOUC (Flash of Unstyled Content) problem, as (at least in this example) instead of inlining styles in <head> it only ads <link preload ... >.

Question is does nextjs somehow block render until styles are loaded or not?

fatawesome commented 3 years ago

Yesterday I investigated it a bit and found out that Next simply places display: none; on <body> until everything is loaded. I believe that is not a good idea for a production application (slow FCP and so on), so I'm planning on writing some plugin which will somehow bundle css on remote in a way that it can be easily consumed on host to be injected in html on server-side.

If anyone here faces the same issue, I hope we can discuss solutions here despite it does not directly relate to node-mf lib and this particular repository.

telenko commented 3 years ago

Ah, was not aware of this behavior on the next.js side. I think for SSR it is more complicated to ensure that contents can be shown only after everything was loaded successfully. Interested in your solution.

phoenix-ru commented 2 years ago

Coming back at the issue. As to anyone interested, we managed to solve the remotes CSS issue in the Vue 3 SSR by inlining the styles programmatically on the server.

It might not be the most optimal solution, as inlining styles isn't as good as using extracted CSS. Anyways, the general process for solving the issue for Vue 3 SFC is:

  1. Use the webpack loader to inject CSS into your component's generated code. I did it by adding a injectStyles function which would require all the component's styles and inject them into the renderer context.
  2. The injectStyles function is then linked to the component lifecycle hook which you know would be executed on the server (beforeCreate for Vue). It is important to have access to SSR context inside the component. In Vue 3 it is done by invoking useSSRContext().
  3. Create a renderer context and pass it to the application (e.g. app = createSSRApp(); html = renderToString(app, ctx)).
  4. After rendering, the styles will be on ctx.styles.

By the way, all the mentioned steps are implemented in Vue 2 already (make sure you have vue-loader v15), but not in Vue 3 (vue-loader v16 doesn't support it, that's pity). For Vue 3, you need to manually add vue-style-loader to automatically collect the styles into a convenient format (of course, you can collect the output of CSSLoader yourself).

I believe as soon as you treat the remote components as render + script + styles (instead of just render + script which is the default behavior of vue-loader@16), it is a relatively straightforward task.