Vue 3 - 如何使模板可与 Composition API 重用?

Vue 3 - how to make templates reusable with the Composition API?

我最近刚刚将我的项目从 Vue 2 迁移到 Vue 3,目前我正在尝试支持 Vue 的新 Composition API。虽然我了解了可组合项等的概念,但有一个使用选项 API 的用例,我还无法弄清楚如何使用新的 API。

我真正喜欢 Options API 的 mixin 的地方在于,它们可以让 met 在基本模板上施加额外的行为,以便在不同的继承组件中重用它们,尽管我也看到了它们带来的问题。 (注意:我最近才发现 extends 应该用于此。)现在有了 Composition API,我不确定如何解决这个用例。举个例子:

BaseComponent.vue

<template>
    <div class="base-template">
        <span @click="baseBar">{{ baseFoo }}</span>
    </div>
</template>

<script>
    export default {
        name: BaseComponent,
        data() {
            return {
                baseFoo: "baseFoo"
            };
        },
        methods: {
            baseBar() {
                alert("baseBar");
            }
        }
    }
</script>

SubComponentA.vue

<script>
    import BaseComponent from './BaseComponent.vue';

    export default {
        name: SubComponentA,
        mixins: [BaseComponent],
        data() {
            return {
                baseFoo: "subFooA"
            };
        },
        methods: {
            baseBar() {
                alert("subBarA");
            }
        }
    }
</script>

SubComponentB.vue

<script>
    import BaseComponent from './BaseComponent.vue';

    export default {
        name: SubComponentB,
        mixins: [BaseComponent],
        data() {
            return {
                baseFoo: "subFooB"
            };
        },
        methods: {
            baseBar() {
                alert("subBarB");
            }
        }
    }
</script>

这样我就可以独立使用 SubComponentASubComponentB,它们都继承自同一个基本模板。所以基本上,我可以在不同的组件中重复使用相同的模板,同时还可以处理基础数据。

现在我不知道如何将此行为转化为合成 API。虽然使用 Composables 将逻辑从 BaseComponent 导入 SubComponentA 是目前我能得到的最接近的方法,但似乎没有办法用它们导出模板结构,更不用说它们也被绑定到导出的逻辑了.我唯一能想到的就是求助于渲染函数,但重建我的模板将是一个主要的时间消耗。

那么这是怎么做到的呢?这甚至是 Composition API 的明智方法,还是我什至错过了重新思考整个概念的一些步骤?

我最终使用的解决方案是将组件的状态存储到它自己的模块中,如下所示:

BaseState.js

import { ref } from 'vue';

export const baseFoo = ref("baseFoo");
export const baseBar = () => alert("BaseBar");

export function setBaseFoo(newBaseFoo) {
    baseFoo.value = newBaseFoo;
}

export function setBaseBar(newBaseBar) {
    baseBar = newBaseBar;
}

BaseComponent.vue

<template>
    <div class="base-template">
        <span @click="baseBar">{{ baseFoo }}</span>
    </div>
</template>

<script setup>
    import { baseFoo, baseBar } from `./BaseState.js`;
</script>

SubComponent.vue

<template>
    <BaseComponent />
</template>

<script setup>
    import { ref } from 'vue';

    import { setBaseFoo, setBaseBar } from `./BaseState.js`;
    import BaseComponent from './BaseComponent.vue';
    
    setBaseFoo("subFoo");
    setBaseBar(() => alert("subBar"));
</script>

不确定这是预期的还是最优雅的解决方案,但对我来说效果很好。

或者您可以使用 defineExpose() 通过模板引用直接访问基本组件的状态逻辑,尽管这个解决方案对我来说似乎不太优雅,因为子组件将无法访问 onMounted().

BaseComponent.vue

<template>
    <div class="base-template">
        <span @click="baseBar">{{ baseFoo }}</span>
    </div>
</template>

<script setup>
    import { ref } from 'vue';

    const baseFoo = ref("baseFoo");
    const baseBar = () => alert("BaseBar");
    
    defineExpose({baseFoo, baseBar});
</script>

SubComponent.vue

<template>
    <BaseComponent ref="base" />
</template>

<script setup>
    import { ref, onMounted } from 'vue';

    import BaseComponent from './BaseComponent.vue';
    
    const base = ref(null);
    
    onMounted(() => {
        const { baseFoo, baseBar } = base.value;
        
        ...
    });
</script>