typescript - 如何使用 Composition API 全局注册 Vue 3 组件?

标签 typescript vue.js vuejs3 vue-composition-api

我正在使用 Vue 3 和组合 API 创建一个组件库。组件库将作为插件加载到 NuxtJS 应用程序中。在这个插件中全局注册组件(根据组合 API 编写)的正确方法是什么?

./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(包含所有组件):

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

export {
    Panel
};

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;

最佳答案

这是一个通用方法,带有一些选项。您可以根据最适合您的方式来决定具体细节。

原理

以正常方式导出的变量<script> block 可以单独导入到组件本身,并且该脚本 block 中定义的任何内容也可以在 <script setup> 中找到。 block 。

方法

组件

组件需要从常规<script>导出元数据。堵塞。 这里有两个选择:直接导出名称(减少样板文件),或导出元数据对象(如果您需要的元数据而不仅仅是名称,则建议您导出)。

只有一个名字:

Panel.vue

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

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

使用元数据对象:

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>

索引

不幸的是,这部分与原始部分相比相当冗长。

只是一个名字

名称必须以某种方式包含在导出中。 这将需要使用单个值的导出,因为我们不仅仅是导出现有的绑定(bind)(例如变量或导入的组件)。 因为在本例中它只是一个名称,所以是一个带有 computed property keys 的简单对象。将工作。 如果需要,您还可以使用“元数据对象”部分中显示的“顶级数组”方法之一。

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

元数据对象

如果您只需要名称,则可以使用上一节中的方法。

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

但是,对于元数据对象,您可能希望将整个对象包含在导出中。

您总共有四个选择。对于顶层,您可以使用以元数据对象中的名称作为键的对象,也可以使用数组。对于值,您可以使用一个对象,其中键是元数据对象的名称,值是保存组件和元数据对象的对象,也可以使用数组。这些选项可以混合和匹配。

使用数组作为值需要它们为 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 },
};

插件

此处使用的内容取决于您使用的是名称还是元数据对象以及用于导出组件的方法。

对于一个名称和简单的对象:

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;

对于其他导出方法之一:

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

您可能会看到迭代和回调参数中的模式。

您还可以使用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);
});

文档

来自API docs for app.component() :

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.

请参阅component registration docs了解更多信息。

关于typescript - 如何使用 Composition API 全局注册 Vue 3 组件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75148146/

相关文章:

visual-studio - Typescript:生成的 javascript 文件在 Visual Studio 中不可见

javascript - bootstrap-vue Form File Upload没有样式,但是其他组件有?

typescript - 什么是 Vue 3 组合 API 定义方法的类型安全方式

javascript - NestJS无法解析JWT_MODULE_OPTIONS的依赖关系(同样的问题,不同的解决方案)

angular - 更改 Angular 7 中父组件中的变量

node.js - 需要带有 TypeScript、SystemJS 和 Electron 的 nodejs "child_process"

javascript - Data 中的属性在methods() 方法中未定义

javascript - vue mapGetters 没有准时到达

javascript - React 有相当于 Vue.set() 的函数吗?

datatable - PrimeVue 编辑行