Injecting CSS imported from JavaScript in a .vue file as critical CSS
Justineo opened this issue · comments
Do you want to request a feature or report a bug?
Feature request (maybe?)
What is the current behavior?
CSS directly imported from JS won't be injected as critical CSS on server side.
What is the expected behavior?
All CSS depended by current component should be injected as critical CSS.
If this is a feature request, what is motivation or use case for changing the behavior?
We have a component library which decoupled with its themes. We put style files into separate NPM packages and made a Babel plugin to rewrite component import statements to use user specified theme. eg.
<script>
import { Button } from 'veui'
// ...
</script>
The script above will be transpiled into the following statements when we configured to use veui-theme-x
:
import { Button } from 'veui'
import 'veui-theme-x/components/button.less'
As a result, it seemed that styles directly imported from JS won't be treated as critical CSS and injected into server-rendered HTML. I tried to modify the following code but it doesn't seem to be working.
Lines 63 to 79 in af61a42
So can vue-style-loader automatically inject this kind of styles into HTML as it does for embedded styles?
Importing CSS from scripts is a common solution for component libraries with multiple themes. Element also suffer from this issue. Actually I'm not sure if this is a problem caused by vue-style-loader
or other tools.
F.Y.I. When the loader's pitch
function runs, the remainingRequest
looks like this.
For styles embedded in <style>
blocks of SFCs:
node_modules/css-loader/index.js?{"minimize":true,"importLoaders":1,"sourceMap":true,"alias":{"/static":"/Users/justice/dev/baidu/one-design/web/static","/assets":"/Users/justice/dev/baidu/one-design/web/assets"}}
node_modules/vue-loader/lib/style-compiler/index.js?{"vue":true,"id":"data-v-fed3543c","scoped":false,"hasInlineConfig":true}
node_modules/stylus-loader/index.js?{"sourceMap":true,"include css":true}
node_modules/vue-loader/lib/selector.js?type=styles&index=0&bustCache
layouts/default.vue
For styles imported from <script>
blocks of SFCs:
node_modules/css-loader/index.js??ref--3-1
node_modules/postcss-loader/lib/index.js??ref--3-2
node_modules/less-loader/dist/cjs.js??ref--3-3
node_modules/veui-theme-x/components/button.less
This only happens if the component is split into an async chunk, because the chunk is not evaluated when the bundle is initially run, direct imports are not registered on the initial render context.
But even if the imports are evaluated, they would be collected as global CSS instead of critical CSS. In order to make them critical CSS, they need to be injected inside the lifecycle hooks of the component they are imported in. This is not what the Babel transform was designed to do.
Usually we recommend importing styles using <style src="...">
, which makes the style to be compiled by vue-loader
and thus properly injected in the component's lifecycle.
For this specific use case, I'd suggest adding a simple loader to pre-process a *.vue
file that does the following:
- Parse for
veui
imports inside the<script>
block - Add accompanying
<style src="...">
links to the file
I'll try the .vue
preprocessor solution to see if it would work as expected. Thanks!
It turns out we don't have to parse imports inside <script>
blocks. We can just inject the corresponding <style>
block when we are loading a component's .vue
file and simple regex matching should be adequate for this scenario.
Hi,
I'am not sure if it's the good place to write this, but I think the information can be maybe usefull to you.
What you want is : "All CSS depended by current component should be injected as critical CSS."
Maybe what you need is : "All CSS depended by current ROUTE should be injected as critical CSS."
You should read this post :
https://vuejsdevelopers.com/2017/07/24/critical-css-webpack/
Context :
Google Lighthouse want us to speed the First meaningful paint.
I try to figure how to fix the "Reduce render-blocking stylesheets" problems in my vue-ssr setup (based on https://github.com/vuejs/vue-hackernews-2.0).
Possible solution :
They use webpack html-critical-webpack-plugin (above link) to detect the critical css (with phantomJs).
In theory, we could detect the critical css for each route, and inject it into a style tag. After we could preload non-critical CSS from a file, instead of be render-blocking.
It's maybe a little bit overkill, and it's not component oriented. It's not perfect, but I think it's maybe the holy grail for the "Reduce render-blocking stylesheets" problem.
If I found how to do it I will reply here
Yes, I agree that prerendering static CSS file for each route may be more performant but it has a prerequisite that the sets of components for different routes should be enumerable. It will absolutely work in some cases though.