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

feat(css): shrink base64 usage in css file #12517

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

sun0day
Copy link
Member

@sun0day sun0day commented Mar 21, 2023

Description

fix #12457

Since css-variable is wildly supported, we can reduce base64 usages in CSS files by extracting base64 into a css-variable.

Additional context

Maybe we can add a new option to tell vite whether to shrink base64 usage or not.


What is the purpose of this pull request?

  • Bug fix
  • New Feature
  • Documentation update
  • Other

Before submitting the PR, please make sure you do the following

  • Read the Contributing Guidelines.
  • Read the Pull Request Guidelines and follow the PR Title Convention.
  • Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
  • Provide a description in this PR that addresses what the PR is solving, or reference the issue that it solves (e.g. fixes #123).
  • Ideally, include relevant tests that fail without this PR but pass with it.

@stackblitz
Copy link

stackblitz bot commented Mar 21, 2023

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@bluwy bluwy added feat: css p2-nice-to-have Not breaking anything but nice to have (priority) labels Mar 23, 2023
@poyoho
Copy link
Member

poyoho commented Apr 5, 2023

I really like this PR because I have done it manually. 😁

This PR transform URL will during at the CSS transform time, but as a user, my favorite thing to write is that different Vue SFCs refer to the same resource. Would it be better to replace them at the render CSS chunk time.

My use case like:

a1.vue

<style scoped>
.bg-1 {
  background: url('../1.png');
}
</style>

a2.vue

<style scoped>
.bg-1 {
  background: url('../1.png');
}
</style>

and use the two compoent in one compoent

<template>
<A1 />
<A2 />
</template>

I guess now will output like

:root{
  --base64-hash1: url(...)
}

.bg-1[filehash-1] {
  background: var(--base64-hash1);
}

:root{
  --base64-hash1: url(...)
}

.bg-1[filehash-2] {
  background: var(--base64-hash1);
}

I think better is

/* inject the css var map in renderChunk? */
:root{
  --base64-hash1: url(...)
}

.bg-1[filehash-1] {
  background: var(--base64-hash1);
}

.bg-1[filehash-2] {
  background: var(--base64-hash1);
}

@garygreen
Copy link

This seems a good PR to bring attention to this one also: #4454

There is a lot of developers hoping Vite will implement an option to emit assets rather than base64-inlining everything.

It's worth noting that base64 inlining has many downsides, which includes:

  • +30% size vs the native format of the jpeg, svg, font, etc. This increases payload size of js and css.
  • Parse time is increased for css and js, as it has to decode the base64 each time
  • Slower build times, as it needs base64 huge payloads into js and css
  • This PR shows another problem: it's difficult to diagnose issues in the css output. It's possible that this PR may not be all that beneficial if assets have already been emitted externally and referenced by standard files, rather than css variables.

@poyoho
Copy link
Member

poyoho commented Apr 6, 2023

yes, svg it seems to can export data:image/svg+xml;charset=utf8, 👀

and other asset should provide config to config the asset is inline or single?

@sun0day
Copy link
Member Author

sun0day commented Apr 6, 2023

This PR transform URL will during at the CSS transform time, but as a user, my favorite thing to write is that different Vue SFCs refer to the same resource. Would it be better to replace them at the render CSS chunk time.

Good point. I think we can transform base64 to css-variable in transform time and emit a extra CSS file containing these variables in :root scope during renderChunk

There is a lot of developers hoping Vite will implement an option to emit assets rather than base64-inlining everything.

I think this is another issue. Let's focus on the minimization of base64 in this PR

@poyoho
Copy link
Member

poyoho commented Apr 6, 2023

This PR transform URL will during at the CSS transform time, but as a user, my favorite thing to write is that different Vue SFCs refer to the same resource. Would it be better to replace them at the render CSS chunk time.

Good point. I think we can transform base64 to css-variable in transform time and emit a extra CSS file containing these variables in :root scope during renderChunk

👍 make sure the logic is not run in the dev serve. I think renderChunk is not export in dev serve.

@sun0day
Copy link
Member Author

sun0day commented Apr 6, 2023

@poyoho cc

chunk.viteMetadata!.importedCss.add(
this.getFileName(
this.emitFile({
name: 'root-css-variable.css',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it better to inject the css in the css chunk, because this css will biger and biger. and the user can't do the loading on demand

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a supplement, the index.html will load 100 assets, but it only need the 10 assets

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I think further, it would be better to keep the transform behavior both in dev and build mode. Otherwise, it may break some plugins that depend on base64 during the transform phase.

Duplicate base64 CSS variables in a CSS chunk will be removed in css-post plugin's renderChunk phase.

@ArnaudBarre
Copy link
Member

ArnaudBarre commented Apr 6, 2023

What is the bundle size impact when the assets are used only once?

I totally get the benefits when the asset is imported twice, but IMO most of the assets are not duplicated. Plus with gzip, the only downside should be parsing time.

Also does this is fine to have few kB in CSS vars? Maybe browser didn't expect this usage (and since I read this article I never trust my feelings on browser perf)

@sun0day
Copy link
Member Author

sun0day commented Apr 6, 2023

I totally get the benefits when the asset is imported twice, but IMO most of the assets are not duplicated. Plus with gzip, the only downside should be parsing time.

We might add an option called build.base64Minify, when the user needs it then turn it on.

Also does this is fine to have few kB in CSS vars? Maybe browser didn't expect this usage

I think it's fine, or do you have some articles that prove me wrong?

@ArnaudBarre
Copy link
Member

I didn't test anything and I think also this should be fine, but my point is that sometimes you can hit an unexpected performance issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat: css p2-nice-to-have Not breaking anything but nice to have (priority)
Projects
Status: Approved
Development

Successfully merging this pull request may close these issues.

assetsInlineLimit资源会重复内嵌
5 participants