使用自定义选项编写Vue插件

时间:2019-11-06 17:26:54

标签: javascript vue.js

我正在尝试编写带有自定义选项的vue插件。我这样做是遵循官方的vue准则(https://vuejs.org/v2/guide/plugins.html),但是找不到定义自定义选项的方法。这些选项应由普通的javascript读取,然后由javascript导出vue组件使用的对象。

我的文件夹结构是这样的:

/src
    factory.js
    CustomComponent.vue

factory.js

import Vue from "vue";
import ImgixClient from "imgix-core-js";

var imgixClient = new ImgixClient({

  domain: CUSTOM_OPTION_URL <-- important bit
  domain: Vue.prototype.$imgixBaseUrl //tried it like this
});

export { imgixClient };

我已经尝试通过在安装方法中利用Vue.prototype设置此自定义位,但似乎无法使其工作

export function install(Vue, options) {
  if (install.installed) return;
  install.installed = true;
  Vue.prototype.$imgixBaseUrl = options.baseUrl;
  Vue.component("CustomComponent", component);
}

1 个答案:

答案 0 :(得分:1)

恐怕这将不是您一直希望得到的简单答案……这里有很多要取消的地方。

让我们从factory.js开始。那不是工厂。这是一个单身人士。单例在依赖项,配置和实例化时间方面存在问题,而这正是您要解决的问题。稍后再讨论。

现在让我们看一下插件。首先,这两行:

if (install.installed) return;
install.installed = true;

那不是必须的。 Vue已经自动执行此操作,应该确保您的插件仅安装一次。也许这来自旧的教程?看一下Vue.use的源代码,没有太多东西了:

https://github.com/vuejs/vue/blob/4821149b8bbd4650b1d9c9c3cfbb539ac1e24589/src/core/global-api/use.js

深入Vue源代码是一个很好的习惯。有时候,它会融化您的思想,但是有些像这样的东西并不难理解。一旦习惯了,不透明的部分就会变得更加清晰。

返回插件。

Vue.prototype.$imgixBaseUrl = options.baseUrl;

尚不清楚为什么要将其添加到原型中。

我假设您已经熟悉JavaScript函数原型的工作原理。

组件实例实际上是Vue的实例。因此,添加到Vue.prototype的任何属性都将被您的组件继承,几乎没有任何开销。考虑以下简单组件:

<template>
  <div @click="onClick">
    {{ $imgixBaseUrl }}
  </div>
</template>
<script>
export default {
  methods: {
    onClick () {
      const url = this.$imgixBaseUrl

      // ...
    }
  }
}
</script>

由于$imgixBaseUrl是继承的属性,因此可以在onClick中通过this.$imgixBaseUrl使用。此外,模板将标识符解析为当前Vue实例的属性,因此{{ $imgixBaseUrl }}也将访问this.$imgixBaseUrl

但是,如果您不需要访问组件中的$imgixBaseUrl,则无需将其放在Vue原型上。您不妨直接将其转储到Vue上:

Vue.imgixBaseUrl = options.baseUrl;

在上面的代码中,我放弃了$,因为不再存在与组件实例属性冲突的风险,而这正是使用原型时激发$的原因。

所以,回到核心问题。

正如我已经提到的,单例在创建时间和配置方面存在重大问题。 Vue具有自己的内置解决方案,可以针对这些“从头开始”的场景。那就是插件。但是,关键功能是插件在您调用install之前不会执行任何操作,从而使您可以控制时间。

原始代码的问题在于,factory.js的内容将在文件导入后立即运行。那将是在安装插件之前,因此尚未设置Vue.prototype.$imgixBaseUrlImgixClient实例将立即创建。它不会等到有人尝试使用它。随后将Vue.prototype.$imgixBaseUrl设置为不会产生任何影响的正确值时,为时已晚。

解决此问题的一种方法(虽然不一定是最好的方法)是延迟实例化ImgixClient。可能看起来像这样:

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

只要在安装插件之前没有任何人调用getClient(),它就应该起作用。但是,这是一个很大的条件。错误地调用它太容易了。除了由此产生的时间耦合外,还通过Vue共享配置来创建更多直接耦合。尽管在自己的小文件中包含ImgixClient实例化代码的想法是很合理的,但只有它独立于Vue才真正值得审查。

相反,我可能只是将实例化移动到插件中,如下所示:

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

我使用default导出并将函数包装在对象中进行了一些表面上的更改,但是如果您更喜欢原始代码中的处理方式,则可以忽略这些更改。

如果组件中需要客户端,则可以通过从原型继承的属性$imgixClient访问该客户端。对于需要访问客户端的任何其他代码,可以从组件中传递,也可以(更可能是)直接从Vue.imgixClient中获取。如果这些用例都不适用,则可以删除插件的相关部分。