Manual inline/critical CSS, or CSS groups
What problem does this feature solve?
Allowing for performance optimisation by picking which CSS to render into the head of the page during SSR, and which to load later.
It would be great if we could choose to tag css to be compiled into a separate file somehow. We could then add the separate CSS to the head of the page during or after SSR rendering
What does the proposed API look like?
Add two style tags to the SFC file, one with a tag, e.g:
<style critical>
...
</style>
<style>
...
</style>
Add a configuration option to control which tags get split. The goal is to have the build generate two or more CSS files so one of them can be rendered into the head of the page during or after SSR
Please use https://github.com/vuejs/rfcs for ideas like this.
If anyone comes across this wanting to do something similar, I created a quick and dirty vite plugin to be placed after the vue plugin. It modifies the SFC code to load the <style critical>
CSS into the ssrContext, which can then be retrieved and injected into the html after renderToString
function performanceOptimisationPlugin(): Plugin {
return {
name: 'vite:performanceOptimisation',
transform(code: string, id: string, ssr?: boolean) {
if (ssr && id.endsWith('.vue') && code.includes('&critical=true')) {
const matches = code.matchAll(/^import "(.*?&critical=true.*?&lang.css)"$/gm)
let imports = ''
const vars = []
for (const match of matches) {
imports += (`\nimport criticalCss${match.index} from "${match[1]}"`)
vars.push('criticalCss'+match.index)
}
if (imports) {
const insert = `\n ;(ssrContext.criticalCss || (ssrContext.criticalCss = [])).push([${vars.join(', ')}].join('\\n'))`
code = code.replace("import { useSSRContext as __vite_useSSRContext } from 'vue'", `$&${imports}`)
code = code.replace('const ssrContext = __vite_useSSRContext()', `$&${insert}`)
const map = this.getCombinedSourcemap()
return { code, map }
}
}
return
},
transformIndexHtml(html, { bundle }) {
if (bundle) {
// If we're building, transform the html to have async JS/CSS
const $ = cheerio.load(html)
$('script').attr('async', 'async')
$('link[rel=stylesheet]').attr('media', 'print').attr('onload', "this.media='all'")
return $.html()
}
}
}