I am debating about how I should design my Vue components. Here is the problem: I have two Vue components that share the same sass. However, each has a slightly different markup, state, and methods. I was wondering how I can reduce the amount of sass code repetition. I don't want to have essentially the same markup and styling twice in two different components. Therefore, I was wondering about what is the best way to go.
Possible Solution 1:
Make one base component that has the necessary sass that is used by both components. Include slots to pass the markup from the other two components. In the other two components, add the base component and pass markup to the slots. This reduces the amount of sass repetition by keeping all the common sass needed in the base component.
Possible Solution 2:
Create an extra sass file with all the common css and then just import it in the component files that need the styling. I am not sure if this is acceptable in the Vue world. I have never seen a project where the author has had a separate file for the sass. It's always just included in the single file component. In the styles directory, people usually only include the global variables and mixins. They don't include styles that are only used in two components.
Possible Solution 3:
Combine the two components into one and use booleans and conditionals to determine what markup and state to use in that one component. I feel like this is bad design because it's essentially like passing a boolean parameter to a function. It violates the Single Responsibility Principle. It diverges the component into two different parts and I feel like it's better to have two components rather than one when this happens in order to prevent the divergence.
Those are the only solutions I can think of so far other than repeating the sass in two different files. I am aware of mixins in sass, but I am not sure where I should place the mixin. Which directory would this go in and how would I organize it? Additionally, I am also aware of mixins in Vue, but I don't think Vue mixins allow sass to be placed in them. This is a big dilemma I'm having right now. Any help is appreciated. Thanks in advance.
Maybe became useful for you to use a module rule at the webpack.config for sass, where all vue components will share the same archive or variable (you choose!);
Explaining better
First, you must have the sass-loader in your app.
To know if you already have it at your application, check out your package.json and search for it. If you haven't... run: npm install -D sass-loader.
Now that you have it, you can set the following configuration at your webpack.config.js:
module: {
rules: [
// ... other rules omitted
{
test: /\.scss$/,
use: [
{
loader: 'sass-loader',
options: {
additionalData: `#import "resources/css/sass/_variables.scss";`
}
}
]
}
]
},
Everything that you put in additionalData is like if you were adding to all Vue Components. It should work fine.
But I don't have a webpack.config.js...
So, if you don't, don't worry. It will be your issue if you started the project using the #vue/cli.
1. Create a file at the root of your vue project called vue.config.js;
2. Paste inside of it the following code:
module.exports = {
css: {
loaderOptions: {
// pass options to sass-loader
// #/ is an alias to src/
// so this assumes you have a file named `src/variables.sass`
// Note: this option is named as "prependData" in sass-loader v8
sass: {
additionalData: `#import "~#/variables.sass"`
},
scss: {
additionalData: `#import "~#/variables.scss";`
},
less:{
// http://lesscss.org/usage/#less-options-strict-units `Global Variables`
// `primary` is global variables fields name
globalVars: {
primary: '#fff'
}
}
}
}
}
There is 3 different options: scss, sass and less. You choose one more convenient for you and delete the others.
I hope to have helped you!
Related
I have a Nuxt project and I need to make custom functions and Vue mixins to reuse in Vue components and pages.
In which folder should I put the files containing these? Should I create a 'lib' folder at the top level of the Nuxt project and put the files in there?
Extra details if that can help:
These functions will be imported only when needed (not global)
These functions will be tested
Nuxt Directory Structure Documentation
in my nuxt project, I usually add four folders at the root directory level of the project which is mixins for all of my mixins, models for the models or classes which I use throughout the app, services which contains all my API's endpoints and utils which contains all my utility functions and other general functions like my input's validation functions and my directory looks like this:
in the case of mixins you can then import the required mixin into the desired component and use them like you normally would:
import someMixin from '#/mixins/someMixin'
...
export default {
mixins: [someMixin],
...
}
you can put the mixins file into 2 folders.
you can create global-mixin.js into the plugins folder and after that set this file into plugins: [] part in nuxt.config. link
you can create a mixins folder and create mixin.js into that. link
But the nuxt.js's documents suggested that the first solution was correct
As Nuxt has not any specific directory for mixins, you can create them like any other plugin you have in your project. I prefer to have my overly used plugins in a folder named common. It is your choice really. But as you want to reuse them throughout your project, then you may want to use global mixins, which are similar, But they can lead to memory leakage when not handled correctly. So we need some kind of flag to prevent it from registering multiple times.
Therefore, create a directory you like (for example myMixinFolder). For example I am going to create a mixin file. I create a file inside myMixinFolder and name it my-mixin-plugin.js.
import Vue from "vue"
if (!Vue.__my_mixin__) {
Vue.__my_mixin__ = true
Vue.mixin({
methods: {
sayIt(name) {
console.log(`Hello dear ${name}`)
}
}
})
}
Then add it to nuxt.config.js file:
plugins: [
{ src: '~/plugins/my-mixin-plugin.js' },
],
Now you can use it in any component like this:
<template>
<span>{{ sayIt('Batman') }}</span>
</template>
Or inside script:
this.sayIt('Batman')
This way you don't need to import mixins again and again (Although, you need to be careful if you have more than one mixin file to prevent memory leakage).
I have an app and would like to include some global styles that I can use anywhere in the app like this:
.btn {
...
}
In webpack I already have this for _variables.scss to include things like $my-color: $fff and that is wired up like this in my loaderOptions:
{
additionalData: `#import "#/styles/_variables.scss";`
}
Obviously for some global styles I could do the same thing, however, this will cause my styles like .btn to load as many times as components that I have.
Logically it would seem best to just go into my root Vue component and add global <style lang="scss"></style>.
However, I am upgrading a legacy jQuery app and it is being piecemealed and instead of one root app component, I have several roots for parts that have been converted to Vue. I don't have a central place to load these styles.
For instance I have searchBar and checkout apps that are instantiated using Vue.extend (so they are all part of the same instance). There aren't just two apps, there are quite a few. If I include the global styles in the root of any of them it... feels... icky...
Is there any way around this or should I set the global styles in a random app with a TODO to refactor once everything is ported over?
Ideally I would do the same thing I'm doing with the _variables.scss but having the styles duplicated for each component is a non starter for me.
In this scenario you do not need to worry about how webpacks CSS loaders are working.
You can simply go into your main.js and import '#/styles/globals.scss' to load the styles globally.
There seem to be certain Components in spartacus that are not meant to be overridden.
For Example every Component used INSIDE "CartDetailsComponent".
For example I need to do a couple of html tweaks on the "cx-cart-item" Component, but unfortunately this component is not served as overridable.
Trying to Reference it in my module with
B2cStorefrontModule.withConfig({
cmsComponents: {
CartItemComponent: {
component: CUSTOMComponent
}
does not have an effect since this component is not referenced in a provider like this
providers: [
provideDefaultConfig(<CmsConfig>{
cmsComponents: {
CartComponent: {
component: CartDetailsComponent,
},
},
}),
],
So the closest Component would be CartDetails (respectively CartComponent).
But this means, that in order to achieve a slightly altered html inside CartItem, I need to override CartComponent, and then copy & paste the entire logic (ts and html) for not only this component, but also CartItemList and finally CartItem itself.
This looks some ridiculously insane amount of effort for a simple html tweak.
Is this really a shortcoming that we just have to deal with right now, or is there another to master such requirements?
You can't customize such components via remapping it like a CMS Component, you should customize it over higher lever CMS Component.
And here you can find GitHub issue https://github.com/SAP/spartacus/issues/4441, which relates to above problem and similar Stack Overflow question - How to customize lower level components in Spartacus?.
I have the feeling I'm thinking way too much into this, but I cannot find what I'm looking for in this pattern.
I have a VueJS app with several components and it all works. I'm using style-resources-loader to pull in my global variables and mixins and such into each component. This works as intended.
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: [
path.resolve(__dirname, './src/styles/variables.scss'),
path.resolve(__dirname, './src/styles/text-mixins.scss'),
path.resolve(__dirname, './src/styles/interactive-mixins.scss'),
],
}
},
}
I also have an app-level style sheet. Resets, general layouts, etc. These are not things I want pulled into the SCSS processing of each component - just something I want output in the final CSS for the application.
Everything I find when looking for "how to add SCSS file to Vue" is all about the resource loader for the component processing. I cannot include the global styles in this way and then rely on de-duping to remove the extraneous ones - the imported global styles are being scoped by the built-in component scoping, which is causing bloat and is just generally a bad pattern.
I also don't want a separate Webpack build and CSS file as the end product if I can avoid it.
I can put this inside say the root level App style block, but that's not a great place to work with page-level CSS. It would be ideal to have this a/a set of SCSS files separate from components, but part of the Vue App's SCSS compiling.
Update
Had a big block of stuff here, not sure how it got in that state but that is not the case now and I cannot recreate it.
Throw them in your entrypoint.
Literally include the scss within the start. Like this in your app.ts or app.js :
import Vue from 'vue'
import Buefy from 'buefy'
import 'buefy/dist/buefy.css' <-- like this
Vue.use(Buefy)
If your webpack is setup correctly, e.g. Vue cli, then it doesn't care how the scss is found. It will just inject it globally. Vue components are also global unless you specify scoped scss.
Example from https://buefy.org/documentation/start/
Final Goal : Include SVG icons from files with the possibility to use CSS on it (mainly on the fill parameter). This seems to mean having a way to inline the SVG file in the Vue template.
I have found plenty of solutions involving webpack loaders (vue-svg-inline-loader, vue-svg-loader, and 2 or 3 others), but I also want to be able to choose dynamically which file I load by using a v-bind directive on the source parameter. Something like :
<img :src="`path/to/${file}.svg`" />
or
<custom-component :src="`path/to/${file}.svg`" />
And none of the webpack loaders seems to allow that. I found no vue plug-in with this feature.
My porject is a quasar-framework project, if this can help.
You can do like that:
<template v-html="svgFile"></template>
...
computed:{
svgFile(){
require(`path/to/${this.file}.svg`)
}
}
But you also need config your webpack to out put your svg as raw html. Using some plugin like svg-inline-loader or whatever you want. See more
{ test: /\.svg$/, loader: 'svg-inline-loader' }