d0st
d0st

Reputation: 126

How to partially load Sass/stylesheet files in a Vue/Webpack webapp with code-splitting

I'm using code-splitting techniques for a web app built with Vue 2 and Webpack 3. The JS chunks work well in an asynchronous manner, giving me a good score on the Lighthouse performance audits.

By opening the 'Coverage' tab on DevTools, however, I can see that about 99% of my CSS styles go unused on the first contentful paint (i.e the first page load). The reason for this is simple: my main.scss file (as shown below) imports all the stylesheets for the project, creating a big chunk of render-blocking code. I've followed a general, popular design pattern for the Sass files but obviously it doesn't fit well in the Component-based pattern used by Vue.

I've tried to load the Component stylesheets from the '.vue' Single-File Components, but I get into another issue: because Webpack parses these files before the main App.vue file where I import my main.scss, all Sass variables used throughout the code go 'undeclared', thus throwing errors all over the place.

My main.scss file looks a bit like this:

//base style
@import 'base/variables';
@import 'base/fonts';
@import 'base/tools';
@import 'base/typography';
@import 'base/general';
@import 'base/buttons';
@import 'base/elements';
// ...

//components style
@import 'components/topnav';
@import 'components/header';
@import 'components/sidemenu';
@import 'components/footer';
@import 'components/login';
// ...

//responsiveness
@import 'base/responsiveness';
// cross browser styles
@import 'base/cross-browser';

Is there a way to split this big file and load styles accordingly and asynchronously, only when they are needed, just like with the other JS chunks?

I don't believe there is some special Webpack loader/plugin that would do this for me, and I'm looking for a solution with the least amount of refactoring to be made. The whole ordeal is increasing my Time to Interactive metrics to about 6-7 seconds.

Upvotes: 3

Views: 1328

Answers (1)

d0st
d0st

Reputation: 126

So: after some fail and error, and after finding this page on vue-loader's docs I think I found a plausible solution, broken down in the following:

  • make sure to have sass-loader and node-sass installed
  • edit the general Webpack configuration as suggested in the documentation, in the above link. Here, in order to include the SASS variables files, for instance, or any other general style sheet you might want (main.scss...), you can do:
         {
            loader: 'sass-loader',
            options: {
              data: `path/to/_variables.scss`,
              includePaths:[__dirname, 'src']
            }
          }
  • next, I broke down the main.scss file: left only the imports of the base styles, such as the Sass variables, fonts, mixins, responsive styles, forms and such.
  • I removed the usual import of the main.scss file in App.vue, which automatically removed the huge 700 KB load in the app.js chunk.
  • Finally, on each route / view template, I added the corresponding component stylesheets in the form of:
<style lang="scss" scoped>
@import 'path/to/main.scss';  // **IF YOU HAVE `vue-cli` VERSION > 3.X, THEN YOU DON'T HAVE TO IMPORT GENERAL STYLE SHEETS SUCH AS THIS**
@import 'path/to/component/component.scss';
...
</style>

The reason why importing the main stylesheet file isn't an issue, is because it's non-blocking code, keeping the execution thread going - differently from before when a huge style sheet blocked the thread and left the page blank for too long. Obviously, Vue's out-of-the-box support of code-splitting in its own .vue files, makes all of this "legit" and super-fast.

Results?

The initial page load was (amazingly) under 1.5 seconds (was 4.5-6), as the Lighthouse performance score hit a solid range of 95-97... on Development environment, where I haven't even enabled JS or text compression! Previously, the score was around 45-55. I'm looking forward to deploying this in the other environments to get some more stats, where more tuning is configured, but it does look a very promising solution.

Upvotes: 1

Related Questions