Add SCSS file to VueJS app, but not each component - vue.js

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/

Related

Where to put custom Vue mixins and helper functions in a Nuxt project?

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).

Vue including global styles without repeating the styles for each component

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.

Vuepress dynamic routes and render as page?

I use vuepress as a mixed solution of static generated pages and dynamic pages in the SPA approach. The dynamic data source is a large database server, hence it can not use the additionalPages feature introduced in vurepress 1.x. That means dynamic routes was added using enhanceApp.js as below. Dynamic pages are rendered with extended layouts to share same page structure (header, footer).
// Foo is a layout component extends from Layout.vue
import Foo from './layouts/Foo.vue'
export default ({
Vue, // the version of Vue being used in the VuePress app
options, // the options for the root Vue instance
router, // the router instance for the app
siteData // site metadata
}) => {
router.addRoutes([
{ path: '/foo/:id', component: Foo },
]);
}
It works, however the layouts/Foo.vue is a component. It missing frontmatter, markdown syntax like normal markdown page does. The question is how to load a markdown page and pass to routes as component?
I'm still new to Vuepress, but I come across this comment in the source code that might be able to help you.
When Vue SFCs are source files, make them as layout components directly
I haven't finished reading all source code, so I'm not very sure I understand it correctly. But I guess when Vuepress find a vue component, it will not render it like a markdown file. It assumes you have your own style inside the .vue file. So I think that may be why you are missing what normal markdown page has.
However, if you need to mix a dynamic data source with static pages, you can try to use Vue in markdown to get what you want to achieve.

How to access a constant in main.js in another js file in vue.js framework?

I have a vue.js application and in the main.js file I have configured the Vuei18n as per the documentation. This is working perfectly and in order to access this in other components, I have added this to new Vue() instance and this is now accessible (with this.$i18n).
Now I have created a validator.js file for validation rules and I need to use the this.$i18n there, but its not working. I have also tried Vue.prototype.$i18n but that too not working. Can someone help me to find out where I went wrong ?
 exporting the i18n variable
I'll assume your app is a vue-cli app, or at least that you use webpack to build it
and therefore can use ES modules.
Working with vue-i18n you would do something like the following:
// step 1: define the i18n object via new Vue18n
const i18n = new VueI18n({
locale: DEFAULT_LANGUAGE,
messages,
});
// step 2: setup the Vue object to use this object:
new Vue({
i18n,
... store, router.... whatever
})
Where messages contain all the JSON's with the translations keys and values.
I guess you already are doing something like this.
And now, in your validator module you need the very same i18n object you plugged to Vue.
Instead of trying to get to Vue in your validator, you can just plain export the i18n varible from main.js:
export {i18n}
So, in your validator.js file, you can just import it:
import {i18n} from '#/main.js' // or whatever is the path
This is pretty much how I'm solving the validator internationalization problem in my own application.
Important note: circular dependencies
If you're already importing the validator.js in your main.js, you won't be able to import i18n from main in your validator module. To avoid such a problem, you would move the i18n initialization and exportation to another module, such as i18n.js, so the validator and the main file can import it independently. 
 Without es6 modules
In case you're not using webpack or another bundler to bundle your code, you can still just export the i18n object to the window object:
window.i18n = i18n
and then use it from whatever file. You just need to be careful to store the i18n object in window before any file will try to access it.
I think what you're looking for is Custom Events. According to this documentation, you should be able to emit your variable with something like below
this.$emit('send-i18n', this.$i18n)
Still according to the documentation, try to use v-on:send-i18n to retreive your value.
Hope it works.

Design: Two Vue Components That Share Similar Sass

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!