Unfortunately, the answer to your question is not as simple as you may have hoped. There are several complexities that need to be addressed.
Let's begin with factory.js
. It's important to note that this is not a factory, but rather a singleton. Singletons can present challenges related to dependencies, configuration, and instantiation timing, which seems to be the issue at hand here. I will delve deeper into this shortly.
Now, turning our attention to the plugin. Firstly, let's examine these two lines of code:
if (install.installed) return;
install.installed = true;
This should not be necessary as Vue handles this automatically to ensure that your plugin is only installed once. It's possible that this practice originated from an outdated tutorial. A look at the source code for Vue.use
reveals that the process is quite straightforward:
https://github.com/vuejs/vue/blob/4821149b8bbd4650b1d9c9c3cfbb539ac1e24589/src/core/global-api/use.js
Exploring the Vue source code can be beneficial. While it may seem daunting at first, there are aspects like this one that are relatively easy to follow. As you become more familiar with it, even the more complex sections start to make sense.
Regarding the plugin:
Vue.prototype.$imgixBaseUrl = options.baseUrl;
The reason for adding this to the prototype is not clear. Assuming you are already acquainted with how JavaScript function prototypes operate.
Component instances essentially derive from Vue
. Therefore, any properties added to Vue.prototype
are inherited by your components with minimal overhead. Consider the following basic component:
<template>
<div @click="onClick">
{{ $imgixBaseUrl }}
</div>
</template>
<script>
export default {
methods: {
onClick () {
const url = this.$imgixBaseUrl
// ...
}
}
}
</script>
Since $imgixBaseUrl
is an inherited property, it can be accessed within onClick
using this.$imgixBaseUrl
. Additionally, templates resolve identifiers as properties of the current Vue instance, so {{ $imgixBaseUrl }}
will also access this.$imgixBaseUrl
.
If the $imgixBaseUrl
is not required within a component, there is no need to place it on the Vue prototype. In such cases, it can be directly assigned to Vue
:
Vue.imgixBaseUrl = options.baseUrl;
In the above code snippet, the dollar sign ($) has been removed since there is no risk of conflicting with component instance properties when using the prototype.
Returning to the main issue:
As mentioned earlier, singletons pose significant challenges regarding creation timing and configuration. Vue offers its own solution for 'do it once at the start' scenarios through plugins. The key advantage of plugins is that they remain inactive until install
is called, allowing control over the timing.
The issue with your original code lies in the fact that the contents of factory.js
execute as soon as the file is imported. This occurs before your plugin is installed, so Vue.prototype.$imgixBaseUrl
remains unset. Consequently, the ImgixClient
instance is created immediately without waiting for utilization. Even if Vue.prototype.$imgixBaseUrl
is set subsequently, it will have no impact as it is too late.
One approach (though not necessarily ideal) to address this would involve lazily instantiating ImgixClient
. This implementation could resemble the following:
import Vue from "vue";
import ImgixClient from "imgix-core-js";
var imgixClient = null;
export function getClient () {
if (!imgixClient) {
imgixClient = new ImgixClient({
domain: Vue.prototype.$imgixBaseUrl
});
}
return imgixClient;
}
Assuming getClient()
isn't invoked prior to installing the plugin, this method should work. However, ensuring this condition is met poses a challenge. Aside from the temporal coupling, sharing configuration through Vue
creates direct coupling which may not be favourable. Although segregating ImgixClient
instantiation code into a distinct file aligns logically, it only holds up if independent of Vue.
Alternatively, relocating the instantiation logic within the plugin might be preferable. The revised code structure could resemble the following:
import ImgixClient from "imgix-core-js";
export default {
install (Vue, options) {
Vue.imgixClient = Vue.prototype.$imgixClient = new ImgixClient({
domain: options.baseUrl
});
Vue.component("CustomComponent", component);
}
}
A few cosmetic changes have been made, utilizing a default
export and encapsulating the function in an object. These adjustments can be disregarded if the original format is preferred.
If the client is required within a component, it can be accessed via the $imgixClient
property inherited from the prototype. For other code snippets requiring access to the client, retrieval can be facilitated either through the component or directly from Vue.imgixClient
. If neither scenario applies, the relevant section of the plugin can be omitted.