简体   繁体   English

如何使用组合 API 全局注册 Vue 3 组件?

[英]How to globally register a Vue 3 component with the composition API?

I'm creating a component library with Vue 3 and the composition API. The component library will be loaded in a NuxtJS application as a plugin.我正在使用 Vue 3 和组合 API 创建组件库。组件库将作为插件加载到 NuxtJS 应用程序中。 What is the right way to globally register the components (written according to the composition API) in this plugin?在这个插件中全局注册组件(根据组合API编写)的正确方法是什么?

./stories/Panel.vue (very reduced version): ./stories/Panel.vue (极简版):

<template>
    <div class="panel">
        <div class="header" v-if="title">{{ title }}</div>
        {{ content }}
    </div>
</template>

<style>
.panel .header {
    background-color: #7f7f7f;
    color: #fff;
}
</style>

<script setup lang="ts">
interface Props {
    title?: string;
    content?: string;
}

const name = 'my-panel';
const props = defineProps<Props>();
</script>

components.ts (contains all components): components.ts (包含所有组件):

import Panel from './stories/Panel.vue';
// all other components are imported and exported as well

export {
    Panel
};

lib.ts : lib.ts

import * as components from './components';
import { App } from '@vue/runtime-core';

const plugin = {
    install(Vue: App) {
        Object.values(components).forEach((component) => {
            // Is there a way to globally register a component in a way
            // that the name, defined in the composition API is used?
            Vue.component(component);
        });
    },
};

export default plugin;

Here is a general method, with some options.这是一个通用方法,有一些选项。 You can decide the specifics based on what works best for you.您可以根据最适合您的方式来决定具体细节。

Principle原则

Variables exported in a normal <script> block can be imported separately to the component itself, and anything defined in that script block is also available in the <script setup> block.在普通<script>块中导出的变量可以单独导入到组件本身,并且在该脚本块中定义的任何内容在<script setup>块中也可用。

Method方法

Components组件

The components will need to have the metadata exported from a regular <script> block.这些组件需要从常规<script>块中导出元数据。 You have two options here: export the name directly (less boilerplate), or export a metadata object (recommended if you are going to need more metadata than just the name).您在这里有两个选择:直接导出名称(较少样板文件),或导出元数据 object(如果您需要的元数据不仅仅是名称,则推荐使用)。

With just a name:只有一个名字:

Panel.vue

<script lang="ts">
export const name = "my-panel";
</script>

<script setup lang="ts">
console.log(name) // `name` is available here.
</script>

With a metadata object:使用元数据 object:

Panel.vue

<script lang="ts">
export const metadata = {
  name: "my-panel",
};
</script>

<script setup lang="ts">
console.log(metadata.name) // `metadata` is available here.
</script>

Index指数

Unfortunately, this part is quite verbose compared to the original.不幸的是,与原始部分相比,这部分非常冗长。

Just a name只是一个名字

The names must be included in the export somehow.名称必须以某种方式包含在导出中。 This will require using an export of a single value, as we are not just exporting an existing binding (such as a variable or the imported components).这将需要使用单个值的导出,因为我们不只是导出现有绑定(例如变量或导入的组件)。 As it is just a name in this case, a simple object with computed property keys will work.由于在本例中它只是一个名称,因此可以使用带有计算属性键的简单 object。 If you want, you could also use one of the "top-level array" methods shown in the "Metadata object" section.如果需要,您还可以使用“元数据对象”部分中显示的“顶级数组”方法之一。

components.ts

// Import the metadata as well.
import Panel, { name as PanelName } from './stories/Panel.vue';

// The export can be default or named.
export default {
  [PanelName]: Panel,
}

Metadata object元数据 object

If you only need the name, you can use the method from the previous section.如果只需要名称,可以使用上一节中的方法。

components.ts

// Import the metadata as well.
import Panel, { metadata as PanelMetadata } from './stories/Panel.vue';

// This works the same as in the previous section.
// As before, the export can be default or named.
export default {
  [PanelMetadata.name]: Panel,
}

However, with a metadata object, you probably want the whole object to be included in the export.但是,对于元数据 object,您可能希望将整个 object 包含在导出中。

You have four options in total.您总共有四个选项。 For the top level, you can use an object keyed with the name from the metadata object, or you can use an array.对于顶层,您可以使用以元数据 object 中的名称作为键的 object,或者您可以使用数组。 For the values, you can use an object with the keys being the names from the metadata objects and the values being an object holding the component and metadata object, or you can use an array.对于值,您可以使用 object,键是元数据对象的名称,值是 object,包含组件和元数据 object,或者您可以使用数组。 These options can be mixed and matched.这些选项可以混合和匹配。

Using arrays for the values requires them to be as const so that TypeScript can infer the correct type.使用 arrays 作为值要求它们是as const ,以便 TypeScript 可以推断出正确的类型。

// Array + array
export default [
  [Panel, PanelMetadata] as const,
];

// Array + object
export default [
  { component: Panel, metadata: PanelMetadata },
]

// Object + array
export default {
  [PanelMetadata.name]: [Panel, PanelMetadata] as const,
};

// Object + object
export default {
  [PanelMetadata.name]: { component: Panel, metadata: PanelMetadata },
};

Plugin插入

What you use here depends on whether you are using a name or a metadata object and on the method you used for exporting the components.您在此处使用的内容取决于您使用的是名称还是元数据 object 以及用于导出组件的方法。

For just a name and simple object:只是一个名字和简单的 object:

lib.ts : lib.ts

// Assuming a default export.
import components from './components';
import { App } from '@vue/runtime-core';

const plugin = {
    // This is the app instance created by `createApp`.
    install(app: App) {
        // Get the `(key, value)` pairs of the export 
        // and register a component for each of them.
        Object.entries(components).forEach(([name, component]) => {
            app.component(name, component);
        });
    },
};

export default plugin;

For one of the other export methods:对于其他导出方法之一:

// Array + array
components.forEach(([component, metadata]) => {
    app.component(metadata.name, component);
});

// Array + object
components.forEach(({ component, metadata }) => {
    app.component(metadata.name, component);
});

// Object + array
Object.values(components).forEach(([component, metadata]) => {
    app.component(metadata.name, component);
});

// Object + object
Object.values(components).forEach(({ component, metadata }) => {
    app.component(metadata.name, component);
});

You can probably see the patterns in the iterating and the callback parameter.您可能会在迭代和回调参数中看到模式。

You could also use Object.entries for top-level objects, but this is a bit less clean:您也可以将Object.entries用于顶级对象,但这不太干净:

// For array values
Object.entries(components).forEach(([name, componentData]) => {
    app.component(name, componentData[1]);

// For object values
Object.entries(components).forEach(([name, componentData]) => {
    app.component(name, componentData.component);
});

Documentation文档

From the API docs for app.component() :来自app.component()的 API 文档:

Registers a global component if passing both a name string and a component definition, or retrieves an already registered one if only the name is passed.如果同时传递名称字符串和组件定义,则注册全局组件,或者如果仅传递名称,则检索已注册的组件。

See the component registration docs for more info.有关详细信息,请参阅组件注册文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM