Vue.js - Making helper functions globally available to single-file components
vue.jsVuejs2Vue ComponentVuexVue Routervue.js Problem Overview
I have a Vue 2 project that has many (50+) single-file components. I use Vue-Router for routing and Vuex for state.
There is a file, called helpers.js, that contains a bunch of general-purpose functions, such as capitalizing the first letter of a string. This file looks like this:
export default {
capitalizeFirstLetter(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
}
My main.js file initializes the app:
import Vue from 'vue'
import VueResource from "vue-resource"
import store from "./store"
import Router from "./router"
import App from "./components/App.vue"
Vue.use(VueResource)
const app = new Vue({
router: Router,
store,
template: '<app></app>',
components: { App },
}).$mount('#app')
My App.vue file contains the template:
<template>
<navbar></navbar>
<div class="container">
<router-view></router-view>
</div>
</template>
<script>
export default {
data() {
return {
// stuff
}
}
}
</script>
I then have a bunch of single-file components, which Vue-Router handles navigating to inside the <router-view>
tag in the App.vue template.
Now let's say that I need to use the capitalizeFirstLetter()
function inside a component that is defined in SomeComponent.vue. In order to do this, I first need to import it:
<template>Some Component</template>
<script>
import {capitalizeFirstLetter} from '../helpers.js'
export default {
data() {
return {
myString = "test"
}
},
created() {
var newString = this.capitalizeFirstLetter(this.myString)
}
}
</script>
This becomes a problem quickly because I end up importing the function into many different components, if not all of them. This seems repetitive and also makes the project harder to maintain. For example if I want to rename helpers.js, or the functions inside it, I then need to go into every single component that imports it and modify the import statement.
Long story short: how do I make the functions inside helpers.js globally available so that I can call them inside any component without having to first import them and then prepend this
to the function name? I basically want to be able to do this:
<script>
export default {
data() {
return {
myString = "test"
}
},
created() {
var newString = capitalizeFirstLetter(this.myString)
}
}
</script>
vue.js Solutions
Solution 1 - vue.js
> inside any component without having to first import them and then prepend this to the function name
What you described is mixin.
Vue.mixin({
methods: {
capitalizeFirstLetter: str => str.charAt(0).toUpperCase() + str.slice(1);
}
})
This is a global mixin. with this ALL your components will have a capitalizeFirstLetter
method, so you can call this.capitalizeFirstLetter(...)
from component methods or you can call it directly as capitalizeFirstLetter(...)
in component template.
Working example: http://codepen.io/CodinCat/pen/LWRVGQ?editors=1010
See the documentation here: https://vuejs.org/v2/guide/mixins.html
Solution 2 - vue.js
Otherwise, you could try to make your helpers function a plugin:
import Vue from 'vue'
import helpers from './helpers'
const plugin = {
install () {
Vue.helpers = helpers
Vue.prototype.$helpers = helpers
}
}
Vue.use(plugin)
In your helper.js
export your functions, this way:
const capFirstLetter = (val) => val.charAt(0).toUpperCase() + val.slice(1);
const img2xUrl = (val) => `${val.replace(/(\.[\w\d_-]+)$/i, '@2x$1')} 2x`;
export default { capFirstLetter, img2xUrl };
or
export default {
capFirstLetter(val) {
return val.charAt(0).toUpperCase() + val.slice(1);
},
img2xUrl(val) {
return `${val.replace(/(\.[\w\d_-]+)$/i, '@2x$1')} 2x`;
},
};
You should then be able to use them anywhere in your components using:
this.$helpers.capitalizeFirstLetter()
or anywhere in your application using:
Vue.helpers.capitalizeFirstLetter()
You can learn more about this in the documentation: https://vuejs.org/v2/guide/plugins.html
Solution 3 - vue.js
Create a new mixin:
"src/mixins/generalMixin.js"
Vue.mixin({
methods: {
capitalizeFirstLetter(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
}
})
Then import it into your main.js like:
import '@/mixins/generalMixin'
From now on you will be able to use the function like this.capitalizeFirstLetter(str)
within your component script or without this
in a template. i.e.:
<template>
<div>{{ capitalizeFirstLetter('hello') }}</div>
</template>
You have to use this
because you mixed a method into the main Vue instance. If there are ways of removing this
it will probably involve something unconventional, this at least is a documented way of sharing functions which will be easy to understand for any future Vue devs to your project.
Solution 4 - vue.js
> Using Webpack v4
Create a separate file for readability (just dropped mine in plugins folder). Reproduced from @CodinCat and @digout responses.
//resources/js/plugins/mixin.js
import Vue from 'vue';
Vue.mixin({
methods: {
capitalizeFirstLetter: str => str.charAt(0).toUpperCase() + str.slice(1),
sampleFunction() {
alert('Global Functions');
},
}
});
Then, import in your main.js or app.js file.
//app.js
import mixin from './plugins/mixin';
USAGE:
Call this.sampleFunction()
or this.capitalizeFirstLetter()
.
Solution 5 - vue.js
Use a global filter if it only concerns how data is formatted when rendered. This is the first example in the docs:
{{ message | capitalize }}
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
Solution 6 - vue.js
Great question. In my research I found vue-inject can handle this in the best way. I have many function libraries (services) kept separate from standard vue component logic handling methods. My choice is to have component methods just be delegators that call the service functions.
Solution 7 - vue.js
Import it in the main.js file just like 'store' and you can access it in all the components.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
store,
router,
render: h => h(App)
})