如何访问全局 Vue 实例以编程方式在 Vue 3 中添加组件



想象一个简单的自定义组件,如下所示: MyComponent.vue

<template>
<p>
<div>Test {{ message }}</div>
<div ref="container"></div>
</p>
</template>
<script>
export default {
data() {
return {
message: ""
};
},
mounted() {
console.log(this.$store);
},
methods: {
show(message) {
this.message = message;
console.log(this.message);
console.log(this.$refs.container);
},
},
};
</script>

我可以在其他模板中像这样使用它:

<template>
<MyComponent />
</template>
<script>
import MyComponent from "./MyComponent";
export default {
components: {
MyComponent,
},
};
</script>

这工作正常。现在我想以编程方式添加此组件。

import { h, render } from "vue";
const vnode = h(MyComponent);
let el = document.createElement("div");
document.body.appendChild(el);
render(vnode, el);
vnode.type.methods.show(message);

这有效,但我无法访问全局添加的实例,如 veux 存储、路由器、注册的组件和指令。 它看起来像是在自己的 Vue 实例上渲染的。如何将全局 vue 实例添加到其中? 此外,消息不会通过 show() 方法更新。 此外,这个.$refs.容器是未定义的。

有没有人想以正确的方式做到这一点? 我能够使用 Vue2 做到这一点,但这在 Vue3 中不起作用

您可以获得任意数量的应用程序,以使用同一商店。让他们从他们都可以访问的位置导入相同的存储实例。例:

const { createApp, toRefs, defineComponent } = Vue;
const { createPinia, defineStore } = Pinia;
const pinia = createPinia();
const useStore = defineStore('my-store', {
state: () => ({ counter: 0 })
})
const App1 = defineComponent({
setup() {
const store = useStore();
return {
add: () => store.counter++,
sub: () => store.counter--
}
}
})
createApp(App1)
.use(pinia)
.mount('#app-1');
const App2 = defineComponent({
template: `<h1>App 2</h1>
<span v-text="counter"></span>`,
setup: () => ({ ...toRefs(useStore())})
})
// let's delay creation of second app by a couple of seconds:
setTimeout(() => {
createApp(App2)
.use(pinia)
.mount('#app-2')
}, 2e3);
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/vue-demi"></script>
<script src="https://unpkg.com/pinia@2.0.11/dist/pinia.iife.prod.js"></script>
<div id="app-1">
<h1>App 1</h1>
<button @click="add">Add</button>
<button @click="sub">Subtract</button>
</div>
<div id="app-2">
<br>
App2 not loaded yet..
</div>

笔记:

  • const { ...stuff } = Vue变得import { ...stuff } from 'vue'.对于"pinia"导入也是如此。
  • const pinia = createPinia()变得import { pinia } from './shared-location'.在'./shared-location'你应该有export const pinia = createPinia()

这两个应用程序都会对共享 pinia 实例上的更改做出反应。当任一应用将应用商店添加到pinia时,另一个应用可以访问该应用商店并能够正常使用它.
如果使用 TypeScript,您可能需要使用declare来键入您的商店。

@tao 谢谢你的想法。 我提出了另一个解决方案。我做了这样的动态组件包装器:

<template>
<component
v-bind="currentProps"
:is="currentComponentName"
ref="currentComponent"
/>
</template>
<script>
import MyComponent from "./MyComponent";
import { getCurrentInstance } from "vue";
export default {
components: {
MessageDialog,
},
data() {
return {
currentProps: {},
currentComponentName: undefined,
};
},
created() {
let p = getCurrentInstance().appContext.config.globalProperties;
p.showMyComponent: this.showMyComponent,    
},
methods: {
async showMyComponent(message, extraProps) {
this.currentComponentName = "MyComponent";
await this.$nextTick(() => {
//wait untill next tick, else not access to  this.$refs.currentComponent
});
return this.$refs.currentComponent.show(message);
},
},
};
</script>

从我的应用程序中的每个位置,我都可以调用this.showMyComponent(),它会显示它。

最新更新