Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance / bundle size #13

Closed
stefanvanherwijnen opened this issue Jun 23, 2022 · 54 comments
Closed

Performance / bundle size #13

stefanvanherwijnen opened this issue Jun 23, 2022 · 54 comments

Comments

@stefanvanherwijnen
Copy link
Contributor

https://github.com/stefanvanherwijnen/qso/issues/11

My preference is to be able to import from quasar (or any other package) and automatically bundle only the used code. However, this depends on the package that is imported (no sideEffects, preferably importing required CSS at component level)

@0x42h
Copy link

0x42h commented Jun 23, 2022

Thanks, @stefanvanherwijnen... Any way to enable minification, atm?

@stefanvanherwijnen
Copy link
Contributor Author

Give Vitrify 0.5.3 a try. It is now enabled by default.
I'll have to do some more extensive testing, but the CSR build should be pretty small now. There was an issue with duplicated Quasar CSS import.

@0x42h
Copy link

0x42h commented Jun 24, 2022

Okay, cool. That's really close now. Given I add media="none" onload="this.media='all'" to the stylesheets, it's doing this now:
image

Compared to a (not so) similar setup in Quasar with Webpack:
image

I wouldn't look at "accessibility", since that's only because some buttons do not have a spoken word in Quasar, that are simply not used in the former project.

I will always see the following error pass by on the "second run" in vitrify build -m ssg:

✓ 404 modules transformed.
'default' is not exported by ../../../../Library/pnpm/global/5/.pnpm/builtins@5.0.1/node_modules/builtins/index.js, imported by ../../../../Library/pnpm/global/5/.pnpm/import-meta-resolve@2.0.3/node_modules/import-meta-resolve/lib/resolve.js
file: $HOME/pnpm/global/5/.pnpm/import-meta-resolve@2.0.3/node_modules/import-meta-resolve/lib/resolve.js:28:7
26: import path from 'node:path'
27: // @ts-expect-error: hush
28: import builtins from 'builtins'
           ^
29: import packageJsonReader from './package-json-reader.js'
30: import {defaultGetFormatWithoutErrors} from './get-format.js'

Is ssg supported at all or do you have any pointers on where to look to get it to work? I tried running a debugger on it, but the "first pass" (CSR, I suppose) runs fine. My debugger breaks on the line of the issue (import builtins from 'builtins'), but not on the "second pass" (SSG). Knowing how to debug the latter, would be a push in the right direction for me...

On the "first pass", the debugger tells me that the import is indeed a function. I don't have a clear understanding of how that would be different for the "second pass". I don't see how that export should, all of a sudden, vanish, while it's pretending to be a browser, I suppose. Is this some babel issue I need to tackle with a plugin for vite, or something?

Any questions or remarks; feel free!

@stefanvanherwijnen
Copy link
Contributor Author

Import-meta-resolve depends on builtins which for whatever reason doesn't work. I've been struggling with the same but I thought I fixed it. SSG builds did work for me in my dev environment. I'll check it out later.

@stefanvanherwijnen
Copy link
Contributor Author

I tried patching import-meta-resolve, but after updating pnpm I kept getting TypeError: p.match is not a function errors.
I decided to remove import-meta-resolve and replace it with a very naive algorithm (check node_modules, if not found throw error) which should work fine.

I'll have a look at the stylesheet loading next week.

If anyone is reading this and is willing to create a proper ESM friendly package resolver, that would be greatly appreciated 😄 .

@0x42h
Copy link

0x42h commented Jun 24, 2022

Here it's not working, when running vitrify build -m ssg:

Uncaught ReferenceError ReferenceError: require is not defined in ES module scope, you can use import instead
    at <anonymous> ($HOME/Development/Play/Quasar/vitrify/dist/ssr/server/vendor.mjs:33:59351)
    at run (node:internal/modules/esm/module_job:197:25)

Update:

My previously assumed presumptions weren't wrong; add commonjs. For those who don't know, in vitrify.config.js:

import commonjs from 'rollup-plugin-commonjs';
...
export default function ({
  mode,
  command
}) {
  return {
    plugins: [
        commonjs()
    ],
    ...
  }

Something somewhere else was screwed in my project. Started a new one and then this worked as I was expecting it to.

@0x42h
Copy link

0x42h commented Jun 24, 2022

@stefanvanherwijnen vite-ssg supports critters, but installing that here doesn't jibe, for inlined critical CSS, so I suppose that's not what it's using, under the hood and couldn't find a trace of it either, actually.

When I try to add some simple plugins (for rollup), I very quickly run into ReferenceError: window is not defined errors. Which seems odd to me, because why would a plugin make window disappear in SSR rendering?

Trying vite-plugin-html gives that, as well as rollup-plugin-critical.

I will probably look further into it tomorrow.

@stefanvanherwijnen
Copy link
Contributor Author

Are you using a project created with @quasar/cli? Try using pnpm create vitrify. Also, if you add --debugto the build command it will show you a readable error, which seems to be a problem with PostCSS requring the commonjs plugin in order to work (but you don't need PostCSS).

In SSR there is no window, it is browser only. Anything that uses browser code will throw errors in SSR mode.

All in all, coding an isomorphic project (SSR + browser compatibility) in which both ESM and CJS (for dependencies) are supported proves to be quite head wrecking 😩 .

I had to fix a dependency issue with @vue/server-renderer, but a project created with create-vitrify should now work in SSG build mode. I'll have a look at critical CSS later, should be pretty straightforward but likely is more difficult than it seems 😅 .

@stefanvanherwijnen
Copy link
Contributor Author

I added critical CSS generation with the critters package, seems pretty good 😄 .
Available in 0.6.0.

Screenshot from 2022-06-29 10-12-28

@0x42h
Copy link

0x42h commented Jun 29, 2022

Hey woaw! Great work man! 🙂

I gotta catch up on some other work, but this might hereby become the basis for my own/new websites. Very exciting. I'll have a look at the commits later, to learn how you strung it all together, just for curiosity's sake. 🙂

Thanks and have a good one, kerel! 😉

Groetjes,
B (0x42h)

@0x42h
Copy link

0x42h commented Jul 2, 2022

Okay, I was about to "jump right in", but I quickly run into an issue.

pm = alias for pnpm*

I do:

% pm create vitrify vitrify-0.6.4
.../Library/pnpm/store/v3/tmp/dlx-40667  |  +59 ++++++
Packages are hard linked from the content-addressable store to the virtual store.
  Content-addressable store is at: $HOME/Library/pnpm/store/v3
  Virtual store is at:             ../../../Library/pnpm/store/v3/tmp/dlx-40667/node_modules/.pnpm
.../Library/pnpm/store/v3/tmp/dlx-40667  | Progress: resolved 59, reused 59, downloaded 0, added 59, done
? Which template would you like to use? Quasar Framework project
? Package name vitrify-0.6.4
? Project product name App
? Project description A Vitrify app
? Author 
% cd vitrify-0.6.4
% pm i
Packages: +312
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Packages are hard linked from the content-addressable store to the virtual store.
  Content-addressable store is at: $HOME/Library/pnpm/store/v3
  Virtual store is at:             node_modules/.pnpm
Progress: resolved 331, reused 312, downloaded 0, added 312, done

dependencies:
+ @fastify/static 6.4.0
+ @quasar/extras 1.14.2
+ @types/node 18.0.0
+ esbuild 0.14.48
+ fastify 4.2.0
+ fastify-plugin 3.0.1
+ quasar 2.7.4
+ rollup 2.75.7
+ sass 1.53.0
+ vite 3.0.0-beta.5
+ vue 3.2.37
+ vue-router 4.0.16

devDependencies:
+ @vue/server-renderer 3.2.37
+ critters 0.0.16
+ typescript 4.7.4
+ vitrify 0.6.4
% vitrify build -m ssg
vite v3.0.0-beta.5 building for production...

... 

ReferenceError: Cannot access 'F' before initialization
    at mu (file://$HOME/Development/Play/Quasar/vitrify-0.6.4/dist/ssr/server/vue.mjs:4:18289)
    at file://$HOME/Development/Play/Quasar/vitrify-0.6.4/dist/ssr/server/vendor.mjs:11:3208
    at ModuleJob.run (node:internal/modules/esm/module_job:197:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:337:24)
    at async CAC.<anonymous> (file://$HOME/Library/pnpm/global/5/.pnpm/vitrify@0.6.4_sivcf4agmmxycgja63b22qg34y/node_modules/vitrify/dist/bin/cli.js:71:30)

pm run dev does work.

Any idea what I'm doing wrong?

UPDATE 1: running --debug, I'm getting:

ReferenceError: Cannot access 'isFunction' before initialization
    at defineComponent (file://$HOME/Development/Play/Quasar/vitrify-0.6.4/dist/ssr/server/vue.mjs:3262:5)
    at file://$HOME/Development/Play/Quasar/vitrify-0.6.4/dist/ssr/server/vendor.mjs:1786:38
    at ModuleJob.run (node:internal/modules/esm/module_job:197:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:337:24)
    at async CAC.<anonymous> (file://$HOME/Library/pnpm/global/5/.pnpm/vitrify@0.6.4_sivcf4agmmxycgja63b22qg34y/node_modules/vitrify/dist/bin/cli.js:71:30)

While in th top of that file:

const isMap = (val) => toTypeString(val) === '[object Map]';
const isSet = (val) => toTypeString(val) === '[object Set]';
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isSymbol = (val) => typeof val === 'symbol';

UPDATE 2:

I feel like an idiot, trying to debug this. I opened up the debugger and ran it through vue.mjs, the module with the above found issue. When I put a breakpoint in the definition of isFunction (line 301), it doesn't break on that breakpoint. It directly breaks in the use of the definition (line 3262). So it logically makes sense that it's not defined, as the compiler is complaining. Yet, I don't understand why line 301 doesn't get executed, when importing the module. Is this something specific to mjs that I'm unaware of, or something?

UPDATE 3: Circular dependency (Unnamed default exports with namespaces, I read)? Two hits on this, while searching the for the above described issue. Wonder why you don't have that, then, though...

@0x42h
Copy link

0x42h commented Jul 6, 2022

@stefanvanherwijnen Stil haven't been able to figure this out. Keep getting the following:

ReferenceError: Cannot access 'isFunction' before initialization
    at defineComponent (file://$HOME/Development/Play/Quasar/test-again/dist/ssr/server/vue.mjs:3262:5)
    at file://$HOME/Development/Play/Quasar/test-again/dist/ssr/server/vendor.mjs:1822:38
    at ModuleJob.run (node:internal/modules/esm/module_job:197:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:337:24)
    at async CAC.<anonymous> (file://$HOME/Library/pnpm/global/5/.pnpm/vitrify@0.6.4_sivcf4agmmxycgja63b22qg34y/node_modules/vitrify/dist/bin/cli.js:71:30)

Any hints?

Also, I see that { $q.version } returns a ?, instead of the version, for some reason.

This is a clean project, made with create-vitrify test-again, cd-ing into it, pnpm i, vitrify build -m ssg --debug or without --debug.

@0x42h
Copy link

0x42h commented Jul 6, 2022

0.6.14
image
image

Thanks!

@stefanvanherwijnen
Copy link
Contributor Author

I spent the past days playing whac-a-mole with node_modules, package resolution, prebundling, SSR constant definition, side effects, manual chunking and tree shaking 😩 . As you have noticed things broke in a 100 different ways during the process. Also, vite beta.7 seemed to break ssr builds so I had to fix vite at version beta.6 for now.

Vitrify 0.6.17 now has working SSG builds. I'll have to check what SSR changes will be introduced in Vite 4 (opt-in in Vite 3: vitejs/vite#8965) and adjust things accordingly. I'll wait until Vite 3 is released before diving in again 😅 .

@0x42h
Copy link

0x42h commented Jul 10, 2022

Last image; haha! Nice, bro! 😄

But yeah, great work! Amazing. I'm going to have a look at your submits, to see your commits, to get an idea of what you're talking about. In fact, let me do that now, because otherwise it's not going to happen.

I got an almost working old quasar/vue/webpack project running in here now, which is really cool, but I think I noticed a thing with CSS in MainLayout.vue not coming along, when rendered in SSG mode and JavaScript disabled in the browser.

Example; following rule in MainLayout.vue:

<style lang="scss" scoped>
* {
  background-color: red!important
}
</style>

CSR:
image

SSG (javascript disabled in browser):
image

So there you go. You do your best, jump through insane hoops, afaicu (still have to look at the code you wrote the past days) and here I go, breaking it again, weeing in your face, while saying; "Great and thank you"! 😉

Hope you don't mind... 😬

Should I open up another case for this or will this do fine, for now and can I help (other than by breaking things)? 😉

Take care, bro! 😃

@0x42h
Copy link

0x42h commented Jul 13, 2022

Pre-Script: Looked at your code; I didn't fully understand it, in a glance, other than that you seem to have been shifting around with the order of things, if I'm not mistaken, right?... I can imagine the challenge in that, though; recognisable. But that's why we enjoy doing this, right; "have to make it work"?! 😉

Update, of which I'm quite sure it's related to the issue described above.

Here's the CSR version:
good

Here's the SSG version:
bad

Don't know if it's clear what's the issue, so let me point it out; by default, the Quasar button groups highlight a little, on hover. In SSG, that doesn't work.

Seems like you took a break, for a moment, from the project; I can only imagine, I'm really thankful, already and it's been more than enough for me to use it in my website(s), so thanks again! 🙂

And again; may I be able to do anything, let me know; let me try to dig in to see what's going on.

As for this "issue"; just for our information!

Have a great one, Stefan! 🙃

@stefanvanherwijnen
Copy link
Contributor Author

Thanks. It looks like some CSS is missing from the generated SSG bundle. I'll have a look at it later. Since you mentioned you disabled javascript, I think the issue might be that the classes are computed properties: https://github.com/quasarframework/quasar/blob/a59f2a180377c5798cfa9601fcdc029403e21990/ui/src/components/btn-group/QBtnGroup.js
I think so much happens under the hood with javascript these days that disabling it isn´t really an option when using a web framework. Just my first thought, I'll have to look into it.

@0x42h
Copy link

0x42h commented Jul 13, 2022

Yeah, I hear what you're saying and I agree, of course, up to a certain extent, because recently I read that they're going to give "safe mode" to politicians (and the likes), to serve the web, without the possibility of getting hacked and tapped.

Not that I expect politicians to be interested in my websites, but, well, who knows, actually; maybe they will...

Other than the argumentation for it in this way; Quasar SSG with webpack does do this correctly, though. But then, still, of course, it's a chose.

So I'm very satisfied with this already, Stefan. Thanks again and we'll see where this goes. Possibly the Quasar team might become interested in this too, because, thus far, they don't have SSG with Vite; so you've already achieved more than they have been able to do. Possibly also/in part, due to the issue mentioned, though, maybe, but yeah, man... Great work... Impressed, especially for single-handedly. Bravo! 😉

@stefanvanherwijnen
Copy link
Contributor Author

I definitely agree with the safe mode concept and if it works with webpack, it should also be possible with Vite. I think the issue might be multiple style blocks, which I'll have to fix.

To the defense of the Quasar team: they have to support the existing community projects so any major changes to the build system are a million times more complicated than when starting a new project such as Vitrify. I'll see what I can do to support their transition to ESM and they obviously are more than welcome to use Vitrify as their build tool.

@stefanvanherwijnen
Copy link
Contributor Author

The style blocks in Vue aren´t extracted when rendering, which means there is no (simple) way to preload the stylesheets: vuejs/core#4385

If you enable javascript, everything should work fine as the .css file is loaded by MainLayout.mjs. I definitely do not want webpack as a dependency so at this moment there is no solution. Maybe Rollup v3 will make it possible to fix this more easily.

@0x42h
Copy link

0x42h commented Jul 14, 2022

Okay, but it seems that this might possibly be "fixed" (made possible) in the future though...

in/appropriate contemporary "joke" intermezzo:
Not that I'm in a hurry or anything, or in a dire situation I actually created myself, but... can't we somehow force the vue/core team to sell their gas... eeh, I mean, software, real cheap, even if they don't want to, after we've slapped every possible sanction on them, while using their software, to force them to develop this faster?

Aghrum... So... Ehm... Where were we?... O, right!... I guess that we'll just have to hang on for a while and show our well-mannered patience and understanding, taking it healthily easy for a bit, while respecting a different stance on certain priorities, more so than we (n)ever do, then...

Thanks again, Stefan!... 😉

Wishing you a nice and warm Christmas this year, already! 😉

@stefanvanherwijnen
Copy link
Contributor Author

stefanvanherwijnen commented Aug 9, 2022

I found a workaround for "safe-mode" SSG (javascript disabled):

  • Apply the styles on page level (i.e. Index.vue, not MainLayout.vue)
  • Instead of importing the .vue file, import a .ts file which imports the .sass and .vue files:
# Index.sass
*
  background-color: red!important
# Index.ts
import "./Index.sass";
import Index from "./Index.vue";

export default Index;
# routes.ts
...
    children: [{ path: "", component: () => import("src/pages/Index.ts") }],
...
  • Set cssCodeSplit to false:
# vitrify.config.ts
...
build: {
  cssCodeSplit: false
}

This will create a single .css chunk and results in a slightly larger bundle size, but you can safely disable javascript and the styles will be applied 😄 .

Edit: Or simpler, just define globalCss or sass.global in VitrifyConfig.

@0x42h
Copy link

0x42h commented Sep 25, 2022

Yo Stefan,

Please forgive me for the long delay; I've been busy...

Somehow, I don't seem to need to do this anymore. The issues that I was seeing before have disappeared, like hovers over BtnGroup components. That was my issues, but with the latest SSG renders, those issues seem to be gone.

Now we have the next issue, which seems to have to do with a race condition, as it happens most of the times, but not always. It (seems to) have to do with the order in which CSS is loaded in SSG mode.

image

index.xxxxxx.css seems to hold the SCSS I put into App.vue.
vitrify.xxxx.css seems to contains SCSS from Quasar.
quasar.xxxxx.css only seems to have fonts and the a css declaration for .material-icons.

Now since I'm loading Quasar before all this, I expect Quasar css to be included before my CSS in App.vue, like in "normal" CSR. However, in SSG (and, of course, in SSR ~ I just checked), the CSS in App.vue is loaded BEFORE Quasar's CSS.

Any ideas?

🥺

😉

@stefanvanherwijnen
Copy link
Contributor Author

I don't think there is a specific order in which css is preloaded :

function renderPreloadLinks(modules, manifest) {

It might also be that the page css is inlined in the html. I'm not sure what is happening.

@0x42h
Copy link

0x42h commented Sep 26, 2022

Well, it's not about preloading. It's about loading CSS in the correct order, which is vital, of course, for overriding styles should work in the correct order.

As expected I have a render, now, with one being (SSR):

image

And the other being (SSG):

image

Due to critters probably doing all sorts of stuff, it's not entirely surprising that some CSS of Quasar ends up in the quasar.xxx.css-file and some other parts in vitrify.xxx.css.

I'll have a go at unraveling the processing-spaghetti of this.

@0x42h
Copy link

0x42h commented Sep 26, 2022

So it goes like this:

put scss in App.vue and it will end up in index.xxxx.css and index.xxxx.css seems to be loaded in random order and most often before the css of Quasar, which then overrides my scss from App.vue, of course. This only happens in CSR, SSR and SSG mode. Dev mode is fine, somehow.

I've tried to debug the issue and I managed to get my debug setup running and breaking correctly, on my breakpoints (in vitrify), but it seems that renderPreloadLinks in vitrify/packages/vitrify/src/vite/vue/ssr/entry-server.ts, that you mentioned earlier, is never called.

I haven't been able to identify (yet) where it does come from.

For the time being, I have placed my code in my layout, so inside the router; that seems to solve the issue.

@0x42h
Copy link

0x42h commented Oct 2, 2022

Okay, so definitely there's an error in the CSS loading order. Even when putting my code into my layout (for which I'm guessing it's a race condition, since this contradicts my previous conclusions).

In dev mode, all is fine. All is as expected. First Quasar CSS is loaded and then my app CSS. In the order of things of how things are supposed to be.
image

However, when I run in SSG mode (and I'm suspecting SSR), I have to add !important to everything that I want to override, since it's loaded in the wrong order, somehow.
image

Next to that, even although I don't have any errors in dev mode, I do get a hydration error in SSG production/mode, also. No idea why this is happening.

image

@stefanvanherwijnen
Copy link
Contributor Author

Does the same thing happen in a CSR/SSR build? If so, it is probably related to rollup.

@0x42h
Copy link

0x42h commented Oct 2, 2022

Hey, hi there. Yeah, I remember that. I opened up the debugger, but couldn't find the issue, other than arriving in rollup that way, yes. So this should be reported to rollup, then?

@stefanvanherwijnen
Copy link
Contributor Author

stefanvanherwijnen commented Oct 2, 2022

I'll try to figure it out next week. For the hydration errors: check if the error provides any useful info.

Edit: hopefully it is an easy fix by reordering the css imports: vitejs/vite#1171 (comment)

@stefanvanherwijnen
Copy link
Contributor Author

@0x42h Could you try to reproduce this in plain vite/vue? For some reason the style blocks seem to be injected before the sass imports in main.ts. So unfortunately it seems like it is not an easy fix

@0x42h
Copy link

0x42h commented Oct 3, 2022

Ei, Stefan. I was just looking at this again, thinking; I'm afraid it's not going to be an easy fix, as I can only imagine that these are dynamic imports.

As per your question: yeah, sure, but please hold for a moment; my best opportunity to do so, is probably going to be next Wednesday.

@stefanvanherwijnen
Copy link
Contributor Author

I think I fixed the problem by moving global SASS (back) to additionalData. I moved away from this method but I can't remember why... but it seems to be working now 🙄 .
Give vitrify 0.10.2 a try.

@0x42h
Copy link

0x42h commented Oct 4, 2022

Still the same issue:

pnpm dev:
Screenshot 2022-10-04 at 14 52 23

vitrify build -m ssg:
image

It's quite simple, really; just create an app and try to override some of quasars default CSS; you won't succeed, unless by using !important.

@stefanvanherwijnen
Copy link
Contributor Author

I think the problem is that the page CSS (in the .vue style block) is extracted and preloaded by Vite by placing it in <head>. Global and Quasar's CSS on the other hand are inline and placed in <body>. If I understand it correctly, inline styles in <body> will override any other styles, such as in <head>.

I also remembered why global sass in additionalData was wrong: it places the global sass (e.g. Quasar's) in every single generated .css file, resulting in a huge bundle. I reverted it back to how it was.

index.html now contains:

...
<link rel="stylesheet" href="/assets/quasar.0645ef0c.css"><link rel="stylesheet" href="/assets/index.77cbdb0b.css"><link 
rel="stylesheet" href="/assets/vitrify.b9f07562.css"></body></html>

The CSS loading order seems to be: App.vue sass > global sass (vitrify.*.css) > dynamic import of Index.vue sass which is placed in <head>. So unfortunately, it does not behave like you would expect (page styles having the highest importance), and this is likely related to the critical CSS (critters cannot know about the page CSS because it is loaded dynamically).

In fact, it is caused by the wrong preloadStrategy of critters. The one thing I can't seem to fix is that the style sheet of App.vue is overridden by the global sass/css (so don't use styles in App.vue 😁 )

Available in vitrify 0.10.3

Ps. CSS 🤢

@0x42h
Copy link

0x42h commented Oct 4, 2022

Great man! Works like a charm!

PS What else would you suggest to CSS? 😉

@stefanvanherwijnen
Copy link
Contributor Author

Well, nothing 😅 . It's just that CSS and I don't get along.

Fortunately there are (CSS) frameworks which take CSS out of my hands. One which I still need to try is unocss.

@0x42h
Copy link

0x42h commented Oct 6, 2022

Yeah, vite-quasar uses that (but it can't do SSG through vite by quasar, also, of course).

Speaking of which: Unfortunately some regression occurred, when you disable JavaScript. The CSS in my Vue components get left out, because they're loaded through a module (index.mjs).

No idea since when this popped up.

@stefanvanherwijnen
Copy link
Contributor Author

stefanvanherwijnen commented Oct 7, 2022

Yes, that problem can't be fixed unfortunately (#13 (comment)). The only workaround is using sass.global in vitrify.config. Maybe a different critical css package which actually renders the page could solve this, but I don't feel qualified to make a good decision on this and critters is also used in vite-ssg.
Actually, I think this can be fixed and in fact is just a simple bug (not loading the manifest correctly). I'll see if I can fix it.

@0x42h
Copy link

0x42h commented Oct 7, 2022

I was just about to respond with, probably my favourite GIF:

But then I read your edit. I'm curious. That would really make my day. Good luck!

@stefanvanherwijnen
Copy link
Contributor Author

stefanvanherwijnen commented Oct 7, 2022

As mentioned, I struggle with CSS 🥲 .
It seems that a simple bug has put me completely on the wrong track. Vite already knows what CSS to load and puts it into the manifest, but I loaded the manifest incorrectly during prerender...
It should work now in 0.10.4

Ps. That GIF perfectly shows I feel sometimes 😬

@0x42h
Copy link

0x42h commented Oct 7, 2022

Yeah, that works now. Great!

Although I wasn't aware, I was asking myself; so is the problem in the SSR-render or in critters? And then I was wonder; but how could it be that it renders anyway with JS then? Doesn't make sense. And/so as I was rewriting my text a couple of times over, also considering to hint to this scene, to which I adhere, with all the crazy stuff that I find, I thought about just sending the GIF instead, until you recanted your reply... 😉

And I hate to do this, of course, but, now again, things get loaded in the wrong order.

  1. You remember what you did last time?
  2. Are you sure it is resolved by the code you modified and not simply a race-condition (for it has returned)?
  3. Do you want me to load up a simple repo, demonstrating the issue?
  4. LET ME KNOW IF AND HOW I CAN HELP ELSE WISE, PLEASE!? 😉
  5. So, please point me to the bit that you've been tinkering with?

Dev mode:
image

SSG mode:
image

PS If you like that (GIF), then you might like this, also. Crunch through the 80's atmosphere for a moment there, please ~ the punchline, imho, is (almost?) prophetic, in spite of the other imaginative sci-fi stuff, although I can only hope for it to be true ~ what a wonderful world it would be (in spite maybe knowing too much (also about history)).

PPS Happy weekend! 😅

@0x42h
Copy link

0x42h commented Oct 7, 2022

Okay, I found something important/interesting, which explains it all, in part.

I made a new project, just to check and I'm getting the same issue; Just create a new project with create-vitrify, pnpm i, pnpm i vitrify@latest.

Modify Index.vue to only hold:

<template>
  <q-page class="row items-center justify-evenly">
    <h1>YO!</h1>
  </q-page>
</template>

<style lang="sass">
h1
  font-size: 10px
</style>

<script setup lang="ts">
</script>

mkdir -p dist/static && http-server dist/static & pnpm build -m ssg

By happenstance, I was reloading the page, while http-server was serving it and noticed that, at some stage, the heading does render at 10px. Then soon after, "the second pass" of rendering, for SSR, it seems, it reverts back to the incorrect order of CSS and renders at 6rem again.

😯 🤷🏼‍♂️

As I was running the http-server in the same terminal in the background as the process building the project (with the last command above), I get a mix of building-progress and http-server request, for which I can see at about where it (temporarily) renders correctly, which is here:

image

At that brief moment, the whole does render correctly. Then I get a similar kinda output from the building process, ending in print of the quasar language object, it seems, with translations. I saw that in the front-end as well.

When I render SSR only, though, it still shows the incorrect 6rem for the H1. So it's definitely something in SSR, that #-ing up.

Possibly a good pointer.

UPDATE

When I Ctrl+c the process at vite v3.0.0-beta.6 building SSR bundle for production..., I can load the page and it renders correctly, which is basically/simply (somewhat of a) CSR, I suppose.

@0x42h
Copy link

0x42h commented Oct 7, 2022

Found it... You're loading up CSS twice. One is in the header (probably critters is doing this) and one before the </body>-tag.

image

When I remove the latter two, at the </body>-tag, all is good.

Update

CSR is fine:
image

SSR is screwing up already, but differently from SSG):
image
I would expect /assets/Index.###.css to be loaded lastly, instead of first, for it contains the CSS from Index.vue (of course).

Also note that in SSR vitrify.###.css is loaded twice, but also/later in the <head></head>. For SSG, both vitrify.###.css and quasar.###.css are loaded twice, but in SSG the second instances are loaded just before the </body>-tag.

So SSR fails and SSG fails, but differently, somehow.

@stefanvanherwijnen
Copy link
Contributor Author

stefanvanherwijnen commented Oct 10, 2022

Could you try updating Vite to 3.1.7? It seems like there has been some changes. For me, the actual CSS of the page seems to get inlined into <head>, so I'm not able to reproduce your issue entirely. This is probably only in SSR dev mode.
If you could share a reproduction that would help as with the latest vitrify, vite should be ^3.1.0.
The problem is basically this: vite injects links in to <head>, and vitrify manually inject the preload-links into head after that. However, it statically replaces the <!--preload-links--> string, which is located before the links vite has injected (this is unexpected but makes sense). This is why you get a difference between CSR and SSR. On top of that, with SSG, critters adds some more preload links to <head> to make it more confusing.

@0x42h
Copy link

0x42h commented Oct 10, 2022

Yeah, I can check it out, but I actually migrated my project to "original quasar" with vite and everything is working smoothly there, to my surprise. Of course that's not ESM-only, but the performance is off the charts. I also noticed a bug, I believe, with dynamic imports, which seem not to be able to include the files in production-mode, also (which is kind of a weird thing I'm trying, which has to do with loading partial (S)HTML files). But let me keep that for later. I'll give it a try and let ya know.

@0x42h
Copy link

0x42h commented Oct 10, 2022

Back again. Same problem.

Just invited you for a test repo.

What you should be getting, as you do with pm dev and pm build -m csr (I aliased pnpmpnmnpmnpnpm...... eh... for obvious reasons?):
image

What you get with anything other than that:
image

If you need anything, gimme a ring.

@stefanvanherwijnen
Copy link
Contributor Author

Is this correct?
CSR:
csr

SSR:
ssr

SSG:
ssg

Unfortunately, there doesn't seem to be a real fix as I don't understand what exactly is happening in Vite. The main problem is that the SSR manifest which contains the files to preload has everything in the wrong order (Index.css, quasar.css, vitrify.css). I can't seem to figure out why and neither how to change it. Simplest workaround is to reverse the array, but that will probably cause other issues.

The same thing happens with critters which seems to inject vitrify.css after Index.css.

My solutions to this problem: filter the preload array such that vitrify.css and quasar.css are excluded and also manually inline Index.css into body during SSG (instead of using critters).
Critical CSS with a framework is problematic anyways, as Quasar's CSS (which confusingly is placed into vitrify.css) is always critical, the largest CSS file and imported globally.

For some reason I can't get Lighthouse to work at the moment, but I'll check what the difference between inlining vitrify.*.css does for performance.

@0x42h
Copy link

0x42h commented Oct 11, 2022

Well, maybe, for some reason, reversing the order seems to perfectly mediate the whole issue here. First quasar, then vitrify (even although some leakage from quasar, somehow (css optimisation doing this, maybe, "somewhy"?)) and then the Index.###.css. Makes perfect sense. And since order is not all but a lot, with CSS, I wouldn't be surprised if this would actually would work for all cases. The order seems fine.

The only question would be, in that case, why is it in the opposite order the way it is, maybe?

Have you committed the change, so that I can pull and test it myself?

By the way: Since I jumped back to "original" Quasar, which works mighty fine, it seems that I will be dropping this for my project, for the moment. It's also nice to have the facilities of the rest of the Quasar framework, like the serviceworkers for pwa, of course. Nonetheless; may I be able to help you out with testing, and such; you know where to find me.

@stefanvanherwijnen
Copy link
Contributor Author

I think I finally found the solution.
hackerman

For some reason, importing .sass inside JavaScript results in some weird behavior in Vite. Unfortunately though, style blocks do not support virtual modules in vite so injecting sass is only possible with additionalData (which doesn't work for reasons mentioned earlier). I finally figured out a way to inject the sass into the Vue RootComponent (only possible when using a SFC) by manually transforming the code with a vite plugin, and this magically fixes the problem.

I'll have to do some more testing, but your test repository seems to work correctly. It's available now in v0.10.5.

Quasar definitely has far more features than Vitrify currently supports. The main thing it is lacking (imo) is good support for SSR/SSG, which is my main focus at the moment. Also, Quasar (obviously) only supports Quasar and fortunately the Vue ecosystem has made some major steps, especially since Vite. Vitrify makes some of Quasar's features available in non-Quasar Vue projects. However, Quasar imo is still a mile ahead of other UI frameworks so that is what I will personally be using.
Features such as service workers will be implemented when I need them, or when someone else adds it through a PR.

@0x42h
Copy link

0x42h commented Oct 11, 2022

Yep. That looks great, thanks!

What I find hard to understand, tho, is why isn't Quasar having this problem? They use Vite and cssnano. The only difference I can spot is cssnano (as to critters here). But what you say, strikes me as if it has something to do with Vite. Even so, it still could, of course, but then I suppose that would be (a bug) in the critters package, correct? Makes sense for the rendering in dev-mode going correct. I can only presume it's not being used, then. Does it use critters in pnpm dev -m ssr? It doesn't seem to, which, again, makes sense, but as I'm modifying this paragraph, it reminds me of the last paragraph here and issue I found in regards to pnpm dev -m ssr. No idea why that, all of a sudden, renders the title at 6rem again, instead. Maybe some pointer on what's wrong on a deeper level here?

For the rest, in regards to Quasar: Yeah, my idea and/so, for obviously reasons, all understandable.

Quasar SSG/SSR: Funny enough, I had no issues doing SSG with Quasar/Vite, since I moved my code back there. I remember it having issues before, but since I switched back, I was ready to go full SSG within a couple of minutes. Dunno what changed, meanwhile (or what I did wrong before).

One last nasty little thing that will probably make your brainstem itch, tho: Try doing pnpm dev -m ssr and ironically find that then again, all of a sudden, CSS is loaded in the wrong order again.

image

I was just looking a little deeper and as you can see, it seems to be injecting quasar and/or vitrify css twice again, in that case, for some reason.

take cover

@stefanvanherwijnen
Copy link
Contributor Author

stefanvanherwijnen commented Oct 14, 2022

@quasar/app imports the generated css file instead of compiling it from the source sass. That method works without problems, but from a puristic standpoint I don't want an extra CSS compilation step.

AFAIK CSS in SSR dev mode doesn't work out of the box at all (there is an open issue at Quasar), so it has to be injected manually. I think this is an easy fix which I'll look into next week. (or maybe it's fixed in Vite and the workaround is not required anymore)

Edit: I think the problem with SSR dev mode is that CSS imports in main.ts are not included. For this, a workaround is applied which manually collects the CSS. However, the collecting order is problematic as it is as follows: global CSS > IndexPage.vue CSS > App.vue CSS > RootComponent.css, so simply reversing it wouldn't work.
The solution was to ditch the CSS import in main.ts and inject it into RootComponent.css instead.

@stefanvanherwijnen
Copy link
Contributor Author

I think I've hit the limits of optimization at the moment. The remaining problems are:

  • the CSS file containingg all of Quasar's CSS is loaded twice (normally and injected by critters), resulting in Reduce unused CSS .
  • the bundled JS file containing all of Quasar's JS is quite large and not everything is used on initial render, resulting in Reduce unused JavaScript.

Serving the assets compressed makes a huge difference in Lighthouse (~25 points), so here are some instructions to use Caddy for that purpose:

Caddyfile

localhost:3000 {
file_server {
  root ./dist/static
}
encode gzip
}
pnpm exec vitrify build -m ssg
caddy run
lighthouse https://localhost:3000 --view --chrome-flags="--ignore-certificate-errors"

Screenshot from 2022-11-10 15-00-01

The mentioned problems can be fixed, but code splitting Quasar will probably lead to all kind of problems which I'd rather avoid at the moment.

@0x42h Can you close this issue if it is solved for you?

@0x42h
Copy link

0x42h commented Nov 10, 2022

Nice work, man. Looks good. An of course I can. Thanks for all your help, Stefan. It's greatly appreciated. Take care!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants