Importing SCSS file in Vue SFC components without duplication with Webpack - vue.js

When I try to access a scss file in all my Vue SFCs the styles are duplicated causing large css bundles and Dev Tools to crash when style debugging
I am using Webpack 4 and webpack-dev-server to build and run development services with hot reload. I did not create the project with Vue CLI.
I have quite a lot of SFCs (~50) and a sass file (index.scss) that contains global styles and variables. I need to be able to use the styles and variables in index.scss across my SFCs. My current approach is using the data option in my Webpack sass-loader.
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.js$/,
loader: 'babel-loader',
},
{
test: /\.scss$/,
use: [
isDev ? { loader: 'vue-style-loader', options: { sourceMap: hasSM }} : {loader: MiniCssExtractPlugin.loader },
{
loader: 'css-loader',
options: {
sourceMap: hasSM
}
},
{
loader: 'sass-loader',
options: {
sourceMap: hasSM,
data: `#import "#/styles/index.scss";`
}
}
]
}
]
}
This is successful, however I am noticing my index.scss styles included in every component. Local development with devserver is almost impossible because the duplication across 50 components is so vast and devtools can't cope. When I do a build to extract the css then I can see the duplication and the app.css file is huge. I can use a minifying process for deployments but this is not suitable at all for local development.
I have tried other approaches such as removing the data option from sass-loader using import ./styles/index.scss in my main.js file instead, however this fails to build because it can't find the variables I use in my SFCs.
Please see my code snippet for roughly how I have my loaders set up. I feel as if there is a better way to do this, whether it's using different loaders/plugins and I am open to using different approaches

I managed to solve. Essentially when every Vue component was being processed then the sass-loader was importing my global sass file that included Variables, Mixins and most importantly my Styles. The import statement in my main.js doesn't work because the Variables and Mixins are not available by the time the component is being processed.
So I only need to import Variables and Mixins in my components and the Styles can be external and included in my main.js. Since Variable and Mixins are just being included when required (with #include statements), then there is no duplication since it's getting compile to CSS.
So I split my Styles and Variables and Mixins into separate variables
Styles => styles.scss
Variable and Mixins => variableMixins.scss
then import ./styles/styles.scss in my main.js
and my webpack sass-loader would be like
{
loader: 'sass-loader',
options: {
sourceMap: hasSM,
data: `#import "#/styles/variableMixins.scss";`
}
}

Been trying to solve this for too long.
This solution works but isn't perfect for me because I want to keep variables and global styles for specific things together.
_typography.scss, for example should contain general styles and variables relating to global line-height and vertical rhythm.
Then more granularly, a component such as _H1.scss would contain it's own sandboxed styles.
I don't want those general styles duplicated for every component.
The value of using CSS is sharing styles and being able to adjust and rebrand in a project globally. Components do the opposite of this by sandboxing everything.
I think CSS is currently at odds with componentized development and perhaps the answer may be to create a new approach where we use 'component ecosystems' to create relationships between otherwise dumb nodes.
A bit like how we use revealing module patterns in JS.

We ended up splitting up css into separate files (one per component), imporing them all into index.scss and importing index in App.vue`
import '#/scss/index.scss'
This eliminated lots of bloat / duplication and significantly simplified workflow: using sourcemaps + workspaces we can now edit scss directly in devtools and it won't break layout or multiply <style> tags or do any other quakery described all over Webpack's and vue-cli's github issues.
Surprisingly, even code-splitting for css still works, and dynamically loaded modules have their separate css files.
If you need scoping (which you'd need), just use id="component-name" on main node and wrap your scss in
#component-name {
...
}
It amazes me how unsupported modern css techniques by Devtools, Webpack etc. Every aspect is full of bugs and only simpliest scenarios sort of work out of the box (if work at all).

Related

How to extract all the CSS to a single file in Nuxt?

I'm currently building a UI Kit for a client who is using ASP.NET for the main application/backend for their project. The UI Kit that I'm building is created using NuxtJS and I want to compile all of the SCSS files along with Bootstrap into a single compiled CSS file, from what I've gathered on the Nuxt Docs they recommend compiling the SCSS files into single CSS files for each component.
Before I start making a mess of the config file, is there a way to compile them into a single file so the client can just enqueue it on their end? It doesn't need to be the most performative which is why we're going to push it into a singular file.
Here is the part of the doc for Nuxt2, I quote
You may want to extract all your CSS to a single file. There is a workaround for this:
nuxt.config.js
export default {
build: {
extractCSS: true,
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.(css|vue)$/,
chunks: 'all',
enforce: true
}
}
}
}
}
}
This part is not directly written but still available for Nuxt3, so I guess that it should work in a similar approach.
There is only one discussion on Nuxt3's repo, but you may start trying the snippet above already, to see if it fill some of your needs.

How to efficiently add global css using nuxt3 and vite?

I have global sass being included in my project and i cant find an efficient way to add it to the project.
there seems to be 2 popular ways to add css in your project.
vite: {
plugins: [svgLoader()],
css: {
preprocessorOptions: {
scss: {
additionalData: `
#import "~/assets/styles/main.scss";
`,
},
},
},
using vite seems to work but it also seems to inject itself into every component i use, so when i generate my project, i can see my css is repeated multiple times, some files as much as 300 times. the issue is found here on vites side https://github.com/vitejs/vite/issues/4448
css: ["#/assets/styles/main.scss"],
the above option seems to not do it for every component, but normal scoped sass in .vue files doesnt pick up sass variables and mixins on compilation using this method
using additionalData adds it to every page. this item is only meant for mixns and vars, which dont translate into permanent css when built.
basically use only vars in mixins in additionalData then use your global.scss in in css

VueJS and SASS - Import and use SASS variables in script

I'm currently working on a VueJS project with a SASS integration, and I would like to get access to the SASS variables inside the script part of the component to make dynamic style calculations. I saw examples of this with pure JS, and I'm trying to translate that to a VueJS component.
So far, I've been able to integrate SASS variables in the style part of the Vue file, but I can't get the variables imported into the script of the component.
I tried the following:
Exporting the variables from my scss file:
:export {
primaryColor: $primary;
}
And then import the file in my component:
import variables from '#/assets/scss/main.scss'
And access it like so: variables.primaryColor
But I can't get it working, variables is always an empty object with no value inside. I'm using Nuxt with the packages node-sass and sass-loader installed. Any help would be appreciated!
If you want to just access the variables from a SASS file you can add this to configureWebpack in your vue.config.js file:
module:{
rules: [{
test: /\.scss$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "sass-loader"
}]
}]
}
Then let's say that you have a file containing your SASS variables called style.sass that looks something like this
$btn-color: #D75893
:export
buttonColor: $btn-color
In your <script> tag you can import your SASS variables file like so
import styleInfo from 'style.sass';
and access your variables like so
console.log("The button color is", styleInfo.buttonColor);
Note: It is not possible to dynamically change SASS variables at run time as the variables are compiled.
Sources: This article from HashRocket and This article from itnext

Dedupe CSS in Vue.js development mode

I am working on a Vue.js project that heavily uses single file components. These components have scss styles associated with them.
In production mode the duplicate css that occurs from importing the same component multiple times is filtered out. But in development mode the same scss is imported multiple times.
This leads to slow downs with the chrome debugger when inspecting and modifying the css.
Does anone know a way to dedupe the css/scss attatched to single file components in developlment mode?
Here is my current vue config:
module.exports = {
lintOnSave: false,
configureWebpack: {
resolve: {
alias: require("./aliases.config").webpack
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
_: "lodash"
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
]
}
Here's how we ended up solving it.
Import only pure SCSS in components (ie. mixins, variables, functions). If a file with CSS is imported in each component the sass loader will NOT dedupe the CSS in development mode.
In your vue config add the following to include you scss variables in every single file component:
module.exports = {
...
css: {
loaderOptions: {
sass: {
data: `
#import "#src/_variables.scss";
`
}
}
},
...
}
Import your global scss in your app entry (main.js or equivalent)
import "bootstrap";
import "#src/global.scss";
In your global.scss file you can import your variables file so that it can also access your scss variables.

Package vue components that use class syntax

I have a hard time packaging our components as an npm package so we can reuse them in other projects.
I have the feeling I searched everywhere on the internet to no avail. I'm suspecting that using our components in the class syntax style makes most, if not even all, examples fail for me.
The final and most successful so far was the one from the Vue documentation
However with that one I get an error:
[!] (buble plugin) SyntaxError: Unexpected character '#'
The reason for that is obviously the class syntax with #Component immediately failing the build. Is there a way to make this work with class syntax?
My component's script part looks like this (nothing special on the css and template parts):
<script>
import { Vue, Component, Prop } from 'vue-property-decorator';
#Component
export default class Checkbox extends Vue {
#Prop({default: false}) checked;
};
</script>
I think that the problem is with installing vue-loader and vue-template-compiler together.
I'm Quoting Vue-loader Vue Docs
The plugin is required! It is responsible for cloning any other rules
you have defined and applying them to the corresponding language
blocks in .vue files. For example, if you have a rule matching
/\.js$/, it will be applied to <script> blocks in .vue files.
After you npm install them, you need to change your webpack.config.js file like this
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [
// ... other rules
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// make sure to include the plugin!
new VueLoaderPlugin()
]
}