我们正在使用 Vue 3、TS 和 Tailwind 开发 Web 组件库。我们正在使用 DefineCustomElement 宏的自定义实现,@tony19 能够提供 vue-router 和 pinia 等插件支持。除老虎机外,每个功能都完美无缺。
这里有一个使用此自定义宏和重现的 @tony19 示例 fork 的链接:https://stackblitz.com/edit/vue-nested-custom-elements-lose-styles-gtj3fy?file=src/App.vue
我该如何解决这个问题?
我试图通过 setup 函数传递槽,然后返回带有 {...slots } 的 h 函数,但没有成功。直到那里,我不知道如何继续。
我遇到了同样的问题,但基于yyx990803评论是不可能的
我的解决办法:
// MyCustomElement.ce.vue
<template>
<div>
<div class="header">
<slot name="header">Default Header</slot>
</div>
<slot></slot>
</div>
</template>
<script setup>
//Code
</script>
// 该函数确实使用共享数据定义自定义元素
function createElementInstance({ component, app, renderOptions = {} } = {}){
return defineCustomElement({
setup() {
const inst = getCurrentInstance()
Object.assign(inst.appContext, app._context)
Object.assign(inst.provides, app._context.provides)
},
render: () => h(component, null, renderOptions)
})
}
import MyCustomElementfrom './MyCustomElement.ce.vue'
const app = createApp()
customElements.define('my-custom-element', createElementInstance({
app,
MyCustomElement,
{
default: () => h('slot'),
header: () => h('slot', { name: 'header' }),
}
}))
上面的方法并不理想,因为我们必须多次定义插槽,但它可以在您需要在自定义元素之间共享相同的
app
实例的情况下使用。
所以你的 main.ts 文件中有类似的东西
defineCustomElement({
styles: component.styles,
props: component.props,
setup(props, { slots }) {
const app = createApp();
plugins.forEach(plugin => {
app.use(plugin);
});
const inst = getCurrentInstance();
Object.assign(inst.appContext, app._context);
Object.assign(inst.provides, app._context.provides);
return () =>
h(component, {
...props,
...slots
});
}
});
如果您检查该插槽变量,它始终为空,即使自定义元素具有类似的内容
<my-component><div>hmmm...</div></my-component>
这似乎是一个错误,实现此功能的另一种方法是将 main.ts 中的设置代码移动到主 App.vue 设置中,并将其用于自定义元素。所以 App.vue 设置会是这样的:
// Plugin initialization and the list can probably be exported from main.ts, putting this here for simplicity
import router from './router';
const plugins = [plugins];
const app = createApp();
plugins.forEach(plugin => {
app.use(plugin);
});
const inst = getCurrentInstance();
Object.assign(inst.appContext, app._context);
Object.assign(inst.provides, app._context.provides);
那么 main.ts 将减少为
const styles: string[] = [];
const modules = import.meta.glob('./**/*.vue');
for (const path in modules) {
const mod = await modules[path]();
styles.push(mod.default.styles);
}
App.styles = [styles.flat().join('')];
customElements.define('swim-app', defineCustomElement(App));
App 组件中定义的任何插槽现在都应该可以工作。您可以忽略上面的样式内容,这只是修复了另一个错误,即子样式不包含在 Shadow dom 样式标记中。