3

Background

Consider the following:

// app.js
import API from 'utils/API';

const api = new API(config.app.urls.api, endpoints, token);

app.provide('$api', api);

Based on everything I have read in the past 24 hours, the above is the advised method of providing a 'service' to Vue.js 3.

However, the issue with the above is that I now cannot access the API instance as the inject method cannot be used outside of a Vue component.

As such, when I want to import the API instance into my Vuex modules, I can't...

Question

Is there an 'advised' way of accessing a provided service outside of a Vue component? Or is one of my proposed solutions below how it should be done?

Possible Solutions

Proposed Solution 1

Instead of providing the service to Vue, we can just add it to the global properties like so:

// app.js
app.config.globalProperties.$api = api;

and then we can access it like so in the store:

// some-vuex-module.js
import { getCurrentInstance } from 'vue';

const api = getCurrentInstance().appContext.config.globalProperties.$api;

I am using the above for certain things in my app but for the API service it just seems wrong to do it this way.

Proposed Solution 2

Another solution I thought of was to just make the API instance available to the window like so:

// app.js
import API from 'utils/API';

const api = window.api = new API(config.app.urls.api, endpoints, token);

app.provide('$api', api);

The above seems like an anti-pattern though...

1 Answer 1

3

The preferable method in modular environment (which Vue 3 setup is commonly is) is to just import api in places where it's used, regardless of whether it's used inside or outside the component.

The solution with Vue global property originated at the time when Vue applications weren't necessarily modular so they relied on Vue instance as global application scope. Currently it's suitable only for properties that are primarily used in a template and require boilerplate code to import and expose them. The example is $t in vue-i18n.

The solution with provide/inject is usable for cases that need dependency injection. A common case is a library that require dependencies to be loosely coupled. Another one is that a dependency depends on component hierarchy and may vary between components.

The solution with window should be avoided, unless application is divided into separate scripts that cannot interact through common JS modules. Even then the problem is that a script that defines global variable should be loaded before those that use it.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you for your answer, and I absolutely agree about the window solution. The issue with importing is that the instance is already instantiated with specific options and I can't import that into the Vuex module... Or can I?
Can you clarify? You need to have export const api = new API(...) in some module, then you can import it where you use it. This should be done in a separate module and not entry point (e.g. main.js) to avoid circular deps
Yes, I was just thinking that - I can just create a singleton/module and import that, I was just hesitant as I wanted to keep all the logic for creating any providers in one place i.e. app.js.
A trouble-free way here is to keep entry point lean and not define services there. You could definitely do it the way you tried and use provide/inject, but only if services are exclusively used inside components - which is not the case most times, notably for Vuex.
Vue leaves it to users. services dir is a common place for this in project structure. I personally stick to the semantics from Angular where dep/service provider is a class or factory function, and a a dep/service itself is an instance of a provider, often a singleton.
|

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.