Я создаю библиотеку компонентов с 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>, можно импортировать отдельно в сам компонент, и все, что определено в этом блоке сценария, также доступно в блоке <script setup>.
Компоненты должны иметь метаданные, экспортированные из обычного блока <script>. Здесь у вас есть два варианта: экспортировать имя напрямую (без шаблона) или экспортировать объект метаданных (рекомендуется, если вам нужно больше метаданных, чем просто имя).
Только с именем:
Panel.vue
<script lang = "ts">
export const name = "my-panel";
</script>
<script setup lang = "ts">
console.info(name) // `name` is available here.
</script>
С объектом метаданных:
Panel.vue
<script lang = "ts">
export const metadata = {
name: "my-panel",
};
</script>
<script setup lang = "ts">
console.info(metadata.name) // `metadata` is available here.
</script>
К сожалению, эта часть довольно многословна по сравнению с оригиналом.
Имена должны быть каким-то образом включены в экспорт. Это потребует использования экспорта одного значения, поскольку мы не просто экспортируем существующую привязку (например, переменную или импортированные компоненты). Поскольку в данном случае это просто имя, подойдет простой объект с вычисляемыми ключами свойств. При желании вы также можете использовать один из методов «массива верхнего уровня», показанных в разделе «Объект метаданных».
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 для app.component():
Регистрирует глобальный компонент, если передается и строка имени, и определение компонента, или извлекает уже зарегистрированный, если передается только имя.
См. документы по регистрации компонентов для получения дополнительной информации.
Код в <script setup> запускается только при создании компонента (событие жизненного цикла), поэтому нет возможности получить эти данные при определении компонента. У меня есть два рабочих решения. Если вы обновите свой вопрос, чтобы уточнить часть имени, я отредактирую свой ответ.
Я переписал свой ответ, @Codekie. Скажи мне, если это поможет в любом случае.
Когда я экспортирую name в обычный script-блок, я получаю Module '"./stories/Panel"' has no exported member 'name' от компилятора Typescript
Это странно. Я попробовал сейчас, и это работает нормально. Не могли бы вы как-нибудь поделиться соответствующим кодом? Может быть, отредактировать это в своем вопросе?
Неважно .. Когда я настроил репо с MRE, это сработало. Я, наверное, забыл export в прошлый раз. Спасибо, за ваш развернутый ответ.
Дело в том, что я хочу использовать не ключ из
components.ts, аname-свойство изPanel.vue, которое может отличаться, и я бы предпочел не поддерживать его в двух местах.