29

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
7
  • did you console.log(this) to see whats going on? Commented Sep 24, 2020 at 5:59
  • this is undefined inside setup() Commented Sep 24, 2020 at 5:59
  • well i guess you cannot access it then When setup is executed, the component instance has not been created yet Commented Sep 24, 2020 at 6:02
  • The root instance would have to exist before the component though Commented Sep 24, 2020 at 6:05
  • 1
    hmm.. but you have access to the props. Maybe you pass your root instance as a prop down to the child? Commented Sep 24, 2020 at 6:08

4 Answers 4

28

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) 
}
Sign up to request clarification or add additional context in comments.

2 Comments

getCurrentInstance() isn't documented on the page you provided?
It was documented, but It's now available in the core module and used internally by the framework and It's no longer available as exposed api, I'll try to find another solution
16

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');
  }
}

2 Comments

I'm guessing if you are looking for a constant, you can also just define it somewhere export const appName = 'Vue3', and then wherever you want to use it, you can import { appName } from 'constant.js'.
Your second solution: const app = createApp(App); app.mount('#app'); app.provide('appName', 'Vue3'); didn't work for me. app.provide('appName', 'Vue3'); should be before app.mount('#app');
4

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()
  }
})

1 Comment

Yeah it seems like the Vue team really didn't want people having global properties like with Vue 2. I even tried doing `setup() { getCurrentInstance().hello = 'abc'} and the object is frozen (unmodifiable), so I can't add that property
2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.