Access this.$root in Vue.js 3 setup() - javascript

In Vue 2, you can access this.$root inside the created hook. In Vue 3, everything that would have gone inside the created hook now goes in setup().
In setup() we don't have access to this, so, how can we access anything on the root instance?
Say, I set a property on the root instance:
const app = createApp(App).mount('#app');
app.$appName = 'Vue3';
I can access this from mounted() with this.$root.$appName, how can I do this in setup()?
UPDATE
I can access it if I import it:
import app from '#/main';
...
setup() {
console.log(app.$appName) // Vue3
But, this is a hassle if I have to do this for every file.
UPDATE 2
Another workaround is to use provide() inside App.vue and then inject() in any other components:
setup() {
provide('$appName', 'Vue3')
setup() {
inject('$appName') // Vue3

You could define global property in vue 3 :
app.config.globalProperties.appName= 'vue3'
With setup (composition api) you could use getcurrentinstance to get access to that property:
import { getCurrentInstance } from 'vue'
...
setup() {
const app= getCurrentInstance()
console.log(app.appContext.config.globalProperties.appName)
Since you're still able to use the options api you could simply do :
mounted(){
console.log(this.appName)
}

It seems you need provide / inject. In your App.vue:
import { provide } from 'vue';
export default {
setup() {
provide('appName', 'vue3')
}
}
Or provide it with your app:
const app = createApp(App);
app.mount('#app');
app.provide('appName', 'Vue3');
And then in any child component where you want to access this variable, inject it:
import { inject } from 'vue'
export default {
setup() {
const appName = inject('appName');
}
}

If all you want is to replace {{ appName }} in any any template with 'Vue 3' (string), without having to import anything, the cleanest way would be using config.globalProperties, as suggested by other answers:
const app = createApp(App).mount('#app');
app.config.globalProperties.appName = 'Vue 3'
However, you should try not to overuse this pattern. It goes against the reusability and modularization principles which drove the development of Composition API.
The main reason why you should avoid polluting globalProperties is because it serves as pollution field across Vue3 apps, so many plugin devs might decide to provide their plugin instance using it. (Obviously, nobody will ever name a plugin appName, so you run no risk in this particular case).
The recommended alternative to globalization is exporting a useStuff() function.
In your case:
export function useAppName () { return 'Vue 3' }
// or even:
export const useAppName = () => 'Vue 3'
In any component:
import { useAppName } from '#/path/to/function'
setup () {
const appName = useAppName()
return {
appName // make it available in template and hooks
}
}
The advantages:
it uses the Composition API naming convention
when sharing something more complex than a primitive (could be a module, a set of functions, a service, etc...) all types are inferred, out of the box. This is particularly useful in setup() functions.
you only expose and scope your stuff where you need it exposed, not in every single component of your app. Another advantage is: if you only need it in setup() function, you don't have to expose it to template or hooks.
Example usage with a random (but real) plugin:
Create a plugin file (i.e: /plugins/gsap.ts):
import gsap from 'gsap'
import ScrollToPlugin from 'gsap/ScrollToPlugin'
// configure the plugin globally
gsap.registerPlugin(ScrollToPlugin)
export function useGsap () {
return gsap
}
In any component:
import { defineComponent } from 'vue'
import { useGsap } from '#/plugins/gsap'
export defineComponent({
setup () {
const gsap = useGsap()
// gsap here is typed correctly (if the plugin has typings)
// no need for casting
return {
gsap // optionally provide it to hooks and template
} // if needed outside setup()
}
})

For anyone wondering how they can simply access this inside setup(), one way is to set this to a memoized variable in the created() hook and use nextTick() to access it:
const app = createApp(App);
app.config.globalProperties.$appName = 'Hello!';
<script>
import { nextTick } from 'vue';
let self;
export default {
name: 'HelloWorld',
setup() {
nextTick(() => console.log(self.$appName)); // 'Hello!'
},
created() {
self = this;
},
};
</script>
#Psidom's answer is better practice in my opinion, but, this is just another way.

Related

Is there a way to encapsulate Vuex store inside Vue plugin (its install function)?

I have a plugin and this plugin uses Vuex
// plugin.js
import Vuex from "vuex";
import store from "./store.js";
export default {
install(Vue, options) {
const storeInstance = new Vuex.Store(store);
Vue.prototype.$store = storeInstance;
}
};
And in that plugin I import a store object.
// store.js
export default {
actions: {
SOME_RANDOM_ACTION({ state, commit }) {
console.log("some random action");
}
}
};
Dispatching actions and using state is fine and works as expected.
But when I add this plugin to another Vue instance that uses vuex, store object re-initializes with new state.
// index.js
import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue";
import plugin from "./plugin.js";
Vue.use(Vuex);
Vue.use(plugin);
new Vue({
// WARN when i uncomment this next line of code Vuex gets re-initialized with new object
// store: new Vuex.Store({ state: { hello: "hix" } }),
components: {
App
}
}).$mount("#app");
When you uncomment store initialization, store that was defined in the plugin is now not available.
Currently, I have these solutions in mind:
Export my plugin store object to index.js main app, and use this store as a module.
Use some other state management.
Is there a way to use Vuex inside my plugin?
https://codesandbox.io/s/vibrant-sanne-67yej?file=/src/main.js:0-371
Vuex plugin uses store option to assign store instance to Vue.prototype.$store, similarly to your own plugin.
If the intention is to use multiple stores, their names shouldn't collide. The key is to name your store object inside plugin something other than $store
Vue.prototype.$myPluginStore = storeInstance;
But this still doesn't encapsulate $myPluginStore inside the plugin, as it is accessible within the app.
// App.vue
computed: {
appState() {
return this.$store.state;
},
pluginState() {
return this.$myPluginStore.state; // this is now accessible within the main app
}
}
It would be a reasonable solution to allow a store to be used as a module of existing store instead of creating a new store, but only when used within one app and not when used as a plugin for a package.
The main problem is that default store instance ($store) can make use of Vuex helpers - mapGetters, etc.
You can take advantage of the install method exposed by the plugin to get access to the store - which should be accessible from your other component.
One possible solution is to register your store in the index.js like:
import Vue from "vue";
import App from "./App.vue";
import store from "./store";
import plugin from "./plugin";
Vue.use(plugin);
new Vue({
store,
components: {
App
}
}).$mount("#app");
You can then expose $doStuff() and get access to $store in the plugin.js
export default {
install(Vue) {
Vue.prototype.$doStuff = function (payload) {
this.$store.dispatch("SOME_RANDOM_ACTION", payload);
};
}
};
The store instance is accessible from your plugin or all the other components.
You can see a working sample here

Set a class to prototype in NuxtJS Vue

How to set a class to prototype correctly in Vue NuxtJS?
I create plugin
nuxt.config.js
plugins: [
{ src: "~/plugins/global.js" },
],
global.js
import Vue from "vue";
import CustomStore from "devextreme/data/custom_store";
//try set in prototype
Vue.use(CustomStore)
have error
A class must be instantiated using the 'new'
I understand that this is not correct, but I can not find anywhere how to initialize it
Vue.use(new CustomStore());
no error but how call?
I want to use something like this in my component
this.dataSource = this.$CustomStore({ ///... settings...// })
I assume that CustomStore is a function, so you can try using Nuxt.js inject() method. This method will make your functions or values available across your app.
~/plugins/global.js
export default ({ app }, inject) => {
// Inject $CustomStore() in Vue, context and store.
inject('CustomStore', CustomStore)
}
then you can use it across your app components.
your component
mounted() {
this.$CustomStore()
},
reference https://nuxtjs.org/docs/2.x/directory-structure/plugins#inject-in-root--context

Add global variable in Vue.js 3

Anybody know how to do add a global variable in Vue 3 ?
in Vue 2 we use this in the main.js file:
Vue.prototype.$myGlobalVariable = globalVariable
The most direct replacement is app.config.globalProperties. See:
https://vuejs.org/api/application.html#app-config-globalproperties
So:
Vue.prototype.$myGlobalVariable = globalVariable
becomes:
const app = createApp(RootComponent)
app.config.globalProperties.$myGlobalVariable = globalVariable
This is scoped to a particular application rather than being global as it was with Vue.prototype. This is by design, all 'global' configuration options are now scoped to an application.
The relevant RFC is here:
https://github.com/vuejs/rfcs/blob/master/active-rfcs/0009-global-api-change.md
Properties added to globalProperties will be available via the component instance for all components within the application. So if you're using the Options API you'll be able to access them using this.$myGlobalVariable, just like you could with Vue.prototype. They'll also be available in the template without the this., e.g. {{ $myGlobalVariable }}.
If you're using the Composition API then you'll still be able to use these properties within the template, but you won't have access to the component instance within setup, so these properties won't be accessible there.
While hacks involving getCurrentInstance() can be used to access globalProperties within setup, those hacks involve using undocumented APIs and are not the recommended approach.
Instead, application-level provide/inject (also discussed in that RFC) can be used as an alternative to Vue.prototype:
const app = createApp(RootComponent)
app.provide('myGlobalVariable', globalVariable)
In the descendant component this can then be accessed using inject. e.g. With <script setup>:
<script setup>
import { inject } from 'vue'
const myGlobalVariable = inject('myGlobalVariable')
</script>
Or with an explicit setup function:
import { inject } from 'vue'
export default {
setup() {
const myGlobalVariable = inject('myGlobalVariable')
// Expose it to the template, if required
return {
myGlobalVariable
}
}
}
Or with the Options API:
export default {
inject: ['myGlobalVariable']
}
Docs: https://vuejs.org/api/application.html#app-provide
The idea here is that the component can explicitly declare the property rather than inheriting it by magic. That avoids problems like name collisions, so there's no need to use a $ prefix. It can also help to make it clearer where exactly a property is coming from.
It is common for the inject function to be wrapped in a composable. For example, the useRoute composable exposed by Vue Router is just a wrapper around inject.
In addition to globalProperties and provide/inject, there are various other techniques that might be used to solve the same problems as Vue.prototype. For example, ES modules, stores, or even global mixins. These aren't necessarily direct answers to the specific question posted here, but I've gone into more detail describing the various approaches at:
https://skirtles-code.github.io/vue-examples/patterns/global-properties.html
Which approach you prefer will depend on your circumstances.
How to add a global variable using Vue 3 and vue-cli (or Vite)
Note: You can drop the dollar sign from your $globalVariable and just use globalVariable, just like in the documentation.
Initially your main.js file looks something like this (adding router for common use case):
import { createApp } from 'vue'
import { App } from './App.vue'
import { router } from './router'
createApp(App).use(router).mount('#app')
To use add the global variable using Vue 3 and the vue-cli or Vite:
import { createApp } from 'vue'
import { App } from './App.vue'
import { router } from './router'
// 1. Assign app to a variable
let app = createApp(App)
// 2. Assign the global variable before mounting
app.config.globalProperties.globalVar = 'globalVar'
// 3. Use router and mount app
app.use(router).mount('#app')
Then to access the variables in components like this:
<script>
export default {
data() {
return {
myVar: this.globalVar
}
}
}
</script>
like in the template like this:
<template>
<h1>{{ globalVar }}</h1>
</template>
And that's it. Happy coding!
About Global Variables and Composition API
According to the very bottom of samayo's answer on this post, global variables are only available on the Options API.
Quoting the bottom of his answer:
Note: This is only for the Options API. Evan You (Vue creator) says: "config.globalProperties are meant as an escape hatch for replicating the behavior of Vue.prototype. In setup functions, simply import what you need or explicitly use provide/inject to expose properties to app.
I recommend to use provide/inject approach as follows :
in main.js :
import {createApp} from 'vue'
let app=createApp({
provide:{
globalVariable:123
}
}).$mount('#app')
in some child or grand-child component do :
export default{
name:'some-compo',
inject:['globalVariable'],
//then access this.globalVariable as property in you component
...
}
for composition api and script setup :
import { inject } from 'vue'
let globalVar=inject('globalVariable')
If possible you should use imports or provide/inject. Another way to define global variables/functions and use them would be using globalProperties (although this seems to be considered more of an anti-pattern). But if a library you use uses globalProperties then you can use it like this. This also works with global functions.
const app = Vue.createApp({})
app.config.globalProperties.$http = () => {} // global function
app.config.globalProperties.$globalVariable = 'Jimmy' // global variable
1. Using options API
mounted() {
console.log(this.$globalVariable)
}
2. Using setup method
<script setup>
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const progressBar = app.appContext.config.globalProperties.$globalVariable
console.log(this.$globalVariable)
</script>
For those of you who are confused about how to access globalProperties in the setup() method, you can use getCurrentInstance() as in the following documentation.
https://v3.vuejs.org/api/composition-api.html#getcurrentinstance
In my case I had to create a global var and get the data from a script.
Used provide and inject:
In main.js:
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App);
app.provide('message',document.querySelector('script[name="nameSCRIPT"]').innerHTML.split('=').slice(1).join('=').slice(1,-1));
app.mount('#app')
In index.html:
<script name="nameSCRIPT">nameSCRIPT="HELLO"</script>
In child component:
inject:['message'],
mounted(){
console.log(this.message)
},

using vuejs mixins in js file

i have a system in which i have to set up some reusable functions to be used all over my application, now i have created a vue mixin in my main main.js file now when i call that function from vue components it works just fine but when i try to call the same function inside a js file i get an error of undefined here's my code
main.js
Vue.mixin({
methods: {
test: function () {
return 1;
},
}
});
Vue Components
//this works
async created() {
alert(this.test());
}
services.js
import { API } from 'aws-amplify';
import { Auth } from "aws-amplify";
import axios from 'axios'
export default {
somefunction(){
//this doesnot work
alert(this.test());
}
}
can someone tell me how can i use vue mixins in regular js files, i looked on the internet but couldn't find anything related to this
// mixin.js
export myMixin = { computed: { foo(): 'hi' } }
Simply create an object (and likely mark it as an export) besides adding it to vue.
It's just an object. It has special names like computed, data etc. in it, but it's just an object.
// usage.vue
import { myMixin } from './path/to/myMixin.js'
console.log( myMixin.computed.foo ) // hi
export default {
mixins: [ myMixin ],
computed: { bar(): { this.foo } // hi
}
In the above example, I'm not using a global mixin, because, to quote vue docs
Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components.
Now, if you really need a global mixin, that's why it's for, but note that to use myMixin outside of vue export default, you'd need to access it via global scope, like window, or import it like above. For more info refer to queries like these: global functions in js.
My preference:
// in a file at /path/to/index.js
export { myMixin1 } from './myMixin1' // ...
// usage.vue
import { myMixin1, myMixin2 } from './path/to'
export default { mixins: [ ... ] }
or alternatively where needed, (because mixins can include other mixins ;) though I find it harder to use them in other JS then
export myMixin1 = { ... }
export myMixin2 = {
mixins: [ myMixin1 ]
// ...
}
// usage.vue
import { myMixin2 } from 'path/to/myMixins'
export default {
mixins: [ myMixin2 ] // contains both 1 and 2
}
Note you can also declare in Vue files (single file components), and then import from them just as they were Javascript.
Also, you (obviously) don't need to export them - they're already useful for separating concerns.
// example.vue
<script>
export myMixin = { ... }
// all mixins can interact with each other
// because in the end, they will be parts of the same object
myToggle = { ... }
mySuperComplicatedBit = { ... }
// so B can call this.A
export default {
mixins: [
myMixin,
myToggle,
mySuperComplicatedBit
],
...
}
</script>
<template> ...
// other.vue or other.js
import { myMixin } from 'path/to/example.vue'
Cheers and good luck
If your mixin is generic you could use a global mixin and access it via the main application. But I don‘t really see the point, then why have a mixin in the first place?
main.js
export default new Vue({
mixins: [YourMixin]
...
})
somecode.js
import vue from ‚./main.js‘
vue.method()
Edit: suggestion
To be honest, I‘d rather turn your implementation around and have a service expose a functionality, which is then integrated to the Vue components via a mixin.
You can call methods in mixins only in Vue components. The role of the mixin is to extend the vue component. I would extract the logic from your mixin in a separate service or utility js and then use it in the mixin and service.js

Using mixins with Vuejs

I'm currently learning how to develop an app with Vuejs. I have a main.js file with the code for setting up Vue.js. I created a new directory /mixins with a new file api.js. I want to use that as mixin so that every component can use a function to access my api. But I don't know how to do it.
This is my /mixins/api.js file:
export default{
callapi() {
alert('code to call an api');
},
};
This is my main.js file:
import Vue from 'vue';
import VueRouter from 'vue-router';
import VueResource from 'vue-resource';
import { configRouter } from './routeconfig';
import CallAPI from './mixins/api.js';
// Register to vue
Vue.use(VueResource);
Vue.use(VueRouter);
// Create Router
const router = new VueRouter({
history: true,
saveScrollPosition: true,
});
// Configure router
configRouter(router);
// Go!
const App = Vue.extend(
require('./components/app.vue')
);
router.start(App, '#app');
How can I include my mixin the right way now, so that every component has access to the callapi() function?
If you want to use a mixin on a specific component, rather than all components, you can do it like this:
mixin.js
export default {
methods: {
myMethod() { .. }
}
}
component.vue
import mixin from 'mixin';
export default {
mixins: [ mixin ]
}
Another thing you might consider is using a component extension design pattern i.e. creating a base component and then inheriting from that in sub components. It's a little more involved but keeps code DRY if you have many components sharing many options and perhaps inheriting the template too.
I've written about it on my blog if you're interested.
You can apply mixin globally using Vue.mixin
api.js
------
export default {
methods: {
callapi() {};
}
}
main.js
-------
import CallAPI from './mixins/api.js';
Vue.mixin(CallAPI)
As the documentation states you should use it carefully:
Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components.

Categories

Resources