Closed westwick closed 7 years ago
I have the same question,but also have no answer.
I basically have the same question as well.
@westwick have you tried - https://github.com/shakacode/sass-resources-loader
I do not know the answer to the main question (global scss load).
But regarding the additional pain point (adjust relative path), maybe you can mitigate it by configuring a Webpack alias.
You would use @import "styles/settings.scss"
in all your files, having a webpack config like this:
{
resolve: {
alias: {
styles: 'path/to/your/styles'
}
}
}
Docs: https://webpack.github.io/docs/resolving.html https://webpack.github.io/docs/configuration.html#resolve-alias
Neither of these solutions solves the fact that styles.scss will be repeated on your page for however many files you include it in. That just seems not thought out and very wasteful.
@ericcirone We have to differenciate:
The OP asked about importing a .scss
for shared variables & settings.
Such a file usually does not contain any CSS, but only SCSS $variables, mixins etc. (as stated by the OP himself) - so there would be no repeated CSS in your final, compiled files.
Granted, it's tedious to add this @import
statement in every component, but it does not change anything about filesize or performance.
You wrote about importing styles.scss
in multiple files - so , judging from the filename, you seem to ask how to deal with shared styles.
For this scenario, you simply do an import of the shared style file in your main.js
file once. As CSS has no local scope, these styles will be available anywhere in your app so you don't have to import them anywhere else.
@jbruni The alias is a good solution to relative paths, yep! :+1:
But you should use an absolute path:
{
resolve: {
alias: {
styles: path.resolve(__dirname, '../src/path/to/your/styles') // relative to the location of the webpack config file!
}
}
}
@LinusBorg i got ya! I'm new to Sass and Vue. My problem was that i'm building a project with Foundation and tried importing in the Foundation main app.scss in all my <style>
blocks so I could use the Foundation variables and was getting repeating code everywhere.
@ericcirone under the current setup you have to go into the foundation scss folder and find the partials from the src folder that contain the variables and the mixins, then import those in every file. You likely do not need to import the entire main.scss file in every partial if that's what you are doing.
Clsoing as this issue seems to be solved.
I'd ask to leave this open as the real issue is being able to use scss variables, mixins in component definitions. Although css automatically cascades down, if you wanted to use a globally defined sass variable inside your component, you will not be able to use the $variable synthaxx.
//global.scss
$white = '#fff'
//component.vue
<style>
.background {
color: $white;
}
</style>
this will fail and a solution to this would turn my world around.
I also think this should remain open. I'd love to be able to use global scss variables throughout components without importing every time.
Well, this is something that vue-loader is not suited to solve - it would have to be solved in sass-loader etc., because each pre processor might have a different syntax/ way of adding such a file.
Stylus-loader offers such a functionality, by the way.
@uptownhr well you can simply import your variables file in each component. It's a one-line copy&paste for each component, but it works.
@LinusBorg wrote
Stylus-loader offers such a functionality, by the way.
Can you point to an example of this with Stylus?
+1 to everyone asking for this issue to remain open, even if means waiting until a pull request eventually finds its way to sass-loader. This functionality should be a Vue product requirement, frankly.
Can you point to an example of this with Stylus?
This is not very well documented, I only read about it in a docs example for using a plugin:
https://github.com/shama/stylus-loader#using-nib-with-stylus
The import
option will load the file "globally" as far as I have tested it.
+1 to everyone asking for this issue to remain open, even if means waiting until a pull request eventually finds its way to sass-loader.
We will not keep issues open for 3rd-party libs that we have no influence on, and likely won't require a change in vue-loader if they and in the 3rd-party lib (such changes would likely be implemented in loader configs, like the above import
for stylus-loader, which vue-loader doesn't "care" about).
You will be better of asking for this in the respective loader's repos.
I too have the same problem. I really want to use a full Vue.JS Webpack stack on my next project. However.. not being able to import SASS variables is almost a deal breaker here. I guess for now I will compile global styles separately and use minimal styling in the components themselves.
There has to be a solution to importing variables and mixins no? Or making them globally available on compile?
I'm not sure if you missed it, so repeating myself again: you can of course simply @import your variables.
This threads was about doing that automatically, which is not possible and frankly, (repeating myself again, see previous reply) cannot be the job of vue-loader to provide that for all the available pre-processors.
I think you guys should seriously reconsider your position on this. At this point, SASS is the preprocessor of choice for at least 50% of devs (or a lot more, depending on which poll you're looking at). I really like keeping my components' css local to that component but it's a PITA when developing to have to import my settings.scss every time I start a new component, and having to take care to make the path right, and then it also makes the code slightly more verbose. Then if you ever move your component's path you have to update that import statement, etc. etc. Without this ability, I've resorted to the more traditional method of having all my scss files totally separate from the components, which defeats a good chunk of the the purpose of using vue-loader / .vue components IMO. Although to be totally fair I have not looked into the other options of getting this to work (such as the stylus one you linked). Either way, thanks for your work on this project.
You easily reduce the pain about the paths with a webpack alias, reducing the statement to e.g. @import "vars"
, working everywhere.
Considering the number of import statements we have in the JavaScript of most components, that should be manageable.
Adding this automatically with vue-loader would be a hack. For starters, `lang="sass" can mean SCSS or SASS syntax, depending on the sass-loader config. So which do we choose when we prep end the @import string to the content of the script tag? We would have to interpret the webpqck settings somehow, etc.
That's a fair point and I think a webpack alias can be a good "workaround". It's definitely more explicit to always use an import statement, which you could argue is better.
Great :)
quick question, when you @import 'vars'
in all your components, will this bloat your build? Meaning, will webpack be able to compile this into a commons?
Concerning filesize, this has no influence if the imported file only contains SCSS variables and no actual CSS markup.
Concerning memory / compile speed, this should be so small as to be neglegable - I haven't seen any issues with it in dev.
There are other parts of the build process that should be optimized if you experience longer (Re)build times in dev. (vendor chunks, DLL splitting ..)
Is there anything more needed to get Webpack aliases working in .vue
components?
I set a resolve.alias
for my style
directory as suggested above, and can import/require from a Javascript file, but if I do @import "styles/_vars.scss"
in a .vue
component, I get a “File to import not found or unreadable” error.
File structure:
webpack.config.js
src/
├ components/
│ └ my-component.vue
└ style/
└ _vars.scss
// webpack.config.js
{
resolve: {
alias: {
'styles': path.resolve(__dirname, './src/style/')
}
}
}
<!-- my-component.vue -->
<style lang="sass">
@import "styles/_vars.scss";
</style>
I’ve tried all kinds of variations on the import directive with no success, and a direct alias to the variables file also produces an error.
It does work using an absolute path (no Webpack alias), but I’d like to avoid the fragility that introduces:
@import "./../style/vars";
Any help appreciated!
@import "~styles/_vars.scss";
works for stylus: in webpack.config:
const stylus_var = path.resolve(__dirname, './client/styl/var.styl')
resolve: {
alias: {
stylus_var,
}
},
in *.vue:
@import '~stylus_var'
About style in vue project, Here's my solution(for now):
1、I have two separate scss file( src/css/style.scss & src/css/vars.scss )
"style.scss" is a common scss file, which styling the main layout and common style;
"vars.scss" define all the common variable ;
2、In Vue component(single page component), I import the vars.scss file as it needed
3、In webpack , I extract all components style , and combine with the style.scss, when I build, all style combine together perfectly
and for now, it works well , even though import the "vars.scss" file did a little extra job, but not bother too much.
and if there're another more perfectly solution, I glad to hear that.
Here's my main webpack config:
var ExtractTextPlugin = require("extract-text-webpack-plugin")
var extractSCSS = new ExtractTextPlugin({filename: 'css/style.css', disable: false, allChunks: true})
entry: {
app: ['./css/style.scss', './main.js'],
vendor: ['vue', 'vue-router', 'axios']
},
rules: [
{
test: /\.scss$/,
use: extractSCSS.extract({
fallback: "style-loader",
publicPath: "/",
use: ['css-loader', 'sass-loader', 'postcss-loader']
})
},
{
test: /\.vue$/,
loader: 'vue-loader',
include: path.join(__dirname, "src"),
exclude: /node_modules/,
options: {
loaders: {
scss: extractSCSS.extract({
use: ['css-loader', 'sass-loader', 'postcss-loader'],
fallback: 'style-loader'
})
}
}
},
]
Well, I have a problem. Two .vue file. a.vue b.vue In a.vue, I define some vars.(scss) And how can I use these vars in b.vue without making another .scss file??? This problem happens when I try to write a module for vue. b.vue is a file in module, so it can't be changed. a.vue is a file in project, and I want to define some vars for b.vue. Sorry for my bad English. :(
Problem can be solved by following way: 1) include global styles in every *.vue file (webpack aliases can be helpfull as described before) 2) remove dublicating in resulting file by optimize-css-assets-webpack-plugin
NOTE: scoped style in vue component can block styles merging
I was able to do this today using sass-resources-loader
as per @sqal 's suggestion.
I changed vue-cli
's familiar loaders
block from the default:
loaders: {
sass: 'vue-style-loader!css-loader!postcss-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!postcss-loader!sass-loader',
}
...to the array/object syntax that you'd use in module.rules.use
. Here's my entire block for vue-loader
:
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
loaders: {
sass: [
'vue-style-loader',
'css-loader',
'postcss-loader',
'sass-loader?indentedSyntax=1',
{
loader: 'sass-resources-loader',
options: {
resources: path.resolve(__dirname, 'app/src/renderer/styles/variables.scss'), // for example
},
},
],
scss: [
'vue-style-loader',
'css-loader',
'postcss-loader',
'sass-loader',
{
loader: 'sass-resources-loader',
options: {
resources: path.resolve(__dirname, 'app/src/renderer/styles/variables.scss'), // for example
},
},
],
},
},
},
},
Note that I also added postcss-loader but you don't have to, and that your resources
can also be an array.
edit: So now any <style lang="scss">
(or sass
) tag will have my variables.scss file available.
@foundryspatial-duncan, thanks for offering a solution to this issue. However, I just tried this and it doesn't seem to work on my side
This is the base config webpack file which came with the cli
var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
}
}
I went into the vueLoaderConfig file to add your code because that's where all the options go (I only added the scss part since I don't plan to use the sass syntax)
var utils = require('./utils')
var config = require('../config')
var isProduction = process.env.NODE_ENV === 'production'
var path = require('path')
module.exports = {
loaders: utils.cssLoaders({
sourceMap: isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap,
extract: isProduction,
}), scss: [
'vue-style',
'css',
'sass',
{
loader: 'sass-resources-loader',
options: {
resources: path.resolve(__dirname, 'assets/styles/variables.scss')
}
}
]
}
and this is the utils.cssLoaders file
var path = require('path')
var config = require('../config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function (_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
Any idea why?
Ah, the example I gave was actually from an electron template but it will work in the webpack-simple
one too.
For the main webpack
template, I think you'd need to modify the generateLoaders
function in utils.js
.
You basically want to add this object:
{
loader: 'sass-resources-loader',
options: {
resources: path.resolve(__dirname, 'path/to/variables.scss'), // for example
},
}
to the end of the array that generateLoaders
returns for scss
.
Another approach would be to maybe do a push
or a concat
on this line:
scss: generateLoaders('sass'),
I can confirm that this works, thanks!
scss: generateLoaders('sass').concat(
{
loader: 'sass-resources-loader',
options: {
resources: path.resolve(__dirname, '../src/style/variables.scss')
}
}
),
It would be great if anyone could submit a PR to include the solution to this page since it seems to be quite a common request.
@foundryspatial-duncan & @NLNicoo amazing solutions. Thank you
I saw this and decided I wanted to:
scss
variables file in each .vue
fileI noticed that since I used vue-cli
to generate my project, I already had an alias to the src
folder.
in webpack.base.conf.js
:
resolve: {
// snip...
alias: {
// snip...
'@': resolve('src')
}
},
So @
in an import points to the src
folder. And all I had to do was:
src/variables.scss
..vue
file like this:
<style lang="scss">
@import '~@/variables.scss';
div { background-color: $a-variable-from-the-file; }
... in case that helps anybody else.
@dad700, that is awesome! Works for .stylus files, too. This was the simplest solution to date.
For anyone trying to figure out why you need to use a tilde ~
to make aliases work check this issue in css-loader
:
https://github.com/webpack-contrib/css-loader/issues/12
css @import is relative to the current directory. For resolving "like a module" you can prefix
~
.
AFAIK it's not documented anywhere.
@dad700, that is perfect! Clean & minimal solution. Big 👍 from me for not over-engineering.
From the /build folder in the project, i went to utils.js and inside the generateLoaders() function, I added this
// This makes my vars and mixins avail throughout my app at a global level
if (loader === 'sass') {
loaders.push({
loader: 'sass-resources-loader',
options: {
resources: [
path.resolve(__dirname, '../src/styles/vars.scss'),
path.resolve(__dirname, '../src/styles/mixins.scss')
]
}
})
}
make sure you also run npm install --save-dev sass-resources-loader
Last thing is to make sure those files exist or you'll get a compile error. i put them in /src/styles but you can change those lines to put em wherever! OH, also pay attention to the order I have in my resources array. I wanted the vars to get parsed first so that they would be avail to be used from within the mixins. Thanks everyone!
:)
I'm trying to use material-component-web in my project with webpack-simple-template. I have successfully include ' @import "material-components-web/material-components-web"; ' in my .vue file with this webpack config:
loaders: {
scss: [
'vue-style-loader', 'css-loader',
{
loader: 'sass-loader',
options: {
includePaths: ["./node_modules"]
}
}, {
...
but it doesn't has a global effect. I still have to import material-components-web or @material/...( individual component ) in every .vue file. As @foundryspatial-duncan suggested, I use sass-resource-loader to load a style.scss file which will import whole material-component-web library. Here is my webpack.config.js
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
sass: ['vue-style-loader', 'css-loader', {
loader: 'sass-loader',
options: {
includePaths: ["./node_modules"],
indentedSyntax: true
}
}, {
loader: 'sass-resources-loader',
options: {
resources: [
path.resolve(__dirname, './src/style.scss'),
],
includePaths: ["./node_modules"]
}
}],
scss: ['vue-style-loader', 'css-loader', {
loader: 'sass-loader',
options: {
includePaths: ["./node_modules"]
}
}, {
loader: 'sass-resources-loader',
options: {
resources: [
path.resolve(__dirname, './src/style.scss'),
],
includePaths: ["./node_modules"]
}
}]
}
}
}
and here is my src/style.scss file
@import "material-components-web/material-components-web";
$mdc-theme-primary: #4eadff; //override default
$mdc-theme-accent: #ff4081;
$mdc-theme-background: #c5ff83;
Error:
ERROR in ./node_modules/sass-loader/lib/loader.js?{"includePaths":["./node_modules"]}!./node_modules/sass-resources-loader/lib/loader.js?{"resources":["/home/sand/Data/sand/Downloads/vue-practice/module2/src/style.scss"],"includePaths":["./node_modules"]}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-1d1f6298","scoped":true,"hasInlineConfig":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/Header.vue
Module build failed:
<template>
^
File to import not found or unreadable: ../material-components-web/material-components-web.
although I have added an includePaths option under sass-resources-loader in webpack.config.js file, webpack could not find material-components-web under node_modules folder . So my question is how to load a global variables scss file which contain an @import to a third-party library to get that library's variables and mixins ? I had take a look at their vue example but since they use webpack 1, most option is not work in webpack 2,3
I solved it. I forget to put '~' sign at import path like this
@import "material-components-web/material-components-web";
Sorry everyone
That is how I import my vars and mixins sass files globally in my vue templates:
{
loader: 'sass-loader',
options: {
indentedSyntax: true,
sourceMap: true,
outputStyle: 'compressed',
includePaths: ['./app/sass', './app/sass/partials'],
data: '@import vars\n@import mixins'
}
}
Hope this helps someone.
@eduardocmoreno awesome find! better than sass-resources-loader
suggested above, which is just adding another dep.
How does it work with Browserify?
@polikin vue-loader
is a Webpack loader.
For Browserify you should be using this: https://github.com/vuejs/vueify
@anish000kumar that solution worked for me but I realized that the styles for any component that I used global SCSS in wasn't being extracted a separate style.css
that contains all of the app's styling. Instead, it's inserted into the site via style
tags. Any reason why that might be the case?
I have a global.scss
file in a styles/scss
folder that imports a lot of smaller global SCSS files, including variables, mixins, and actual styling I want applied across the app.
In webpack.config.js
my vue-loader
configuration looks like this:
{
test: /\.vue$/,
exclude: /(node_modules|bower_components)/,
loader: 'vue-loader',
options: {
extractCSS: true,
loaders: {
scss: 'vue-style-loader!css-loader!sass-loader?data=@import "./static/styles/scss/global.scss";'
}
}
}
@tomanistor I have updated my answer in detail over here - https://stackoverflow.com/a/46015906/5013932 I guess it would help you. You would want to look into utils/build.js to see how webpack spits the CSS in a separate stylesheet. A quick look reveals this:
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
I guess that should suffice would needs :)
I find myself repeating this same pattern in every component:
My settings.scss only contains variables. An additional pain point is that I have my components nested in several folders for better organization, so I always have to be careful to specify the path to my settings.scss properly. It'd be great if there was a way to globally include a settings.scss or similar file.