We are trying to update a library and the new version requires Vue 3 instead of Vue 2, namely tinymce-vue. Unfortunately, this is a company project using bootstrap-vue, which is not yet fully compatible with Vue 3 (bootstrap-vue3 is not yet production ready and we use some components that have not been migrated yet).
Migrating the complete application to Vue 3 is the main attempt. However, it doesn't allow using Bootstrap components in Vue 3, or if using compatibility mode, part of the app works, but the parts that require that component don't appear/work, or other parts of the component that require Vue 3 are broken. Is there any way to provide library-specific compatibility, or in this case, what is the recommended approach when two libraries require two different versions of Vue in the same component?
I'm not sure if this question should be asked differently, this is my first question in StackOverflow, so if I need to rephrase it or provide more details, please let me know.
P粉0463871332023-12-29 15:53:24
The problem is that it is difficult or even impossible for Vue 2 and 3 applications to coexist in the same project because they depend on vue
packages with the same name but different versions. Even it is possible to use vue
package aliases with different names or use modular Vue (import Vue from 'vue'
) as a version and Vue CDN (window.Vue
) For another version in first-party code, another issue that needs to be addressed is that the Vue library needs to use a specific Vue version.
This requires building and bundling the sub-application using its preferred Vue version and library, which is very close to the concept of a micro-frontend application.
Suppose you have a Vue 3 sub-application that uses a Vue 3 specific library (tinymce-vue) and is specifically written to expose all public APIs to communicate with the outside world:
let MyV3Comp = { template: `<div>{{ myV3Prop }} {{ myV3Data }}</div`, props: ['myV3Prop'], emits: ['myV3Event'], setup(props, ctx) { const myV3Data = ref(1); const myV3Method = () => {}; ctx.emit('myV3Event', Math.random()); // Component public api needs to be exposed to be available on mount() instance ctx.expose({ myV3Data, myV3Method }); return { myV3Data, myV3Method } }, }; // Sub-app entry point let createMyV3App = initialProps => createApp(MyV3Comp, initialProps); export default createMyV3App;
Vue 2 wrapper components act as a bridge between a Vue 3 sub-application and the rest of the Vue 2 application:
import createMyV3App from '.../my-v3-app-bundled'; let MyV2WrapperComp = { template: `<div ref="v3AppWrapper"></div>`, props: ['myV2Prop'], emits: ['myV2Event'], data() { return { myV2Data: null }; }, methods: { // Sync wrapper events onMyV3Event(v) { this.$emit('myV2Event', v); } }, watch: { // Sync wrapper props and data myV2Data(v) { this.v3AppCompInstance.myV3Data.value = v; }, myV2Prop(v) { // Hacky! Better use data and methods from public api to pass info downwards this.v3AppCompInstance._instance.props.myV3Prop = v; }, }, mounted() { // Vue 3 automatically translates onMyV3Event prop as myV3Event event listener // Initial prop values make app props reactive // and allow to be changed through _instance.props this.v3App = createMyV3App({ onMyV3Event: this.onMyV3Event, myV3Prop: null }); // also available as undocumented this.v3App._instance.proxy this.v3AppCompInstance = this.v3App.mount(this.$refs.v3AppWrapper); // Sync wrapper data // Hacky! Better use event from public api to pass info upwards this.v3AppCompInstance._instance.proxy.$watch('myV3Data', v => this.myV2Data = v); }, unmounted() { this.v3App.unmount(); }, };
If the wrapper and sub-application require additional synchronization based on specific points, such as provide
/inject
, template references, etc., this needs to be implemented specifically. In this regard, it's no different from the Vue 3->Vue 2 adapter or adapters involving other frameworks (Angular, React).