Proposed migration plan as of July 23, 2021 (some of the comments below may be contradictory and confusing, because I was working my way to this and hit some dead ends):
Based on all that, I think our migration plan could look something like:
1. Write a wrapper for mounting components that mocks something like the Vue 3 API for Vue 2, and that mounts the component as a child of the given element, rather than replacing the element (i.e. the Vue 3 behavior rather than the Vue 2 behavior). For a draft of this wrapper, see [[https://gerrit.wikimedia.org/r/c/mediawiki/core/+/666460/9/resources/src/vue/index.js|here]].
2. Make all MW code that mounts components use this wrapper.
3. Upgrade the version of Vue that is bundled with MediaWiki (and exposed as the `vue` ResourceLoader module) from version 2.6 of `vue` to version 3.2(*) of `@vue/compat` (once it's out), configured for Vue 2 mode. At the same time, change the wrapper to use the Vue 3 createApp API, and monkey-patch `new Vue()` and `new Vue().$mount()` to behave like Vue 2 rather than Vue 3 (for some reason, the Vue 3 compat build doesn't provide compatibility here). For a draft of what the wrapper would look like at this point, see [[https://gerrit.wikimedia.org/r/c/mediawiki/core/+/666434/8/resources/src/vue/index.js|here]].
4. Check that everything still works, and make minimal changes to fix things where necessary. There will be a million migration warnings, but that's OK.
5. Change all code that uses Vuex to use the `createMwApp()` wrapper. This is necessary because Vuex 4 doesn't support `new Vue( { store: store, ... } )`, not even when using the Vue 3 compat build, and there seems to be no way for us to monkey patch that compatibility in either.
6. Upgrade Vuex from 3.1.3 to 4.0.2 (or whatever the latest version is by then). Make the `createMwApp()` wrapper backwards compatible, so that `createMwApp( { ..., store: store } )` still works. (Vuex 4 instead requires `createMwApp( { ... } ).use( store )`.) For a draft of this wrapper code, see [[https://gerrit.wikimedia.org/r/c/mediawiki/core/+/709125/2/resources/src/vue/index.js|here]].
7. Migrate things one by one, addressing their migration warnings until there aren't any left. This means migrating individual apps, whether they're using a build step or not, and the component library. These can be migrated piecemeal in any order.
- Once a component is migrated, it should set `componentConfig: { MODE: 3 }` in its component options. This ensures that compat features that get in the way of migrated code (such as `ATTR_FALSE_VALUE`) are disabled, and helps test that the component will work correctly in Vue 3.
- The component library can be migrated to Vue 3 directly (using the compat build as an aid, but without ever shipping a release of the library that uses Vue 3-compat), or built in Vue 3 from the ground up. Either way, it will need to set `compatConfig: { MODE: 3 }` on every component to make things work in MediaWiki as long as MW still uses the compat build. This could be done in the library or in MW's wrappers for the library.
8. Once everything is migrated, try setting the global `compatConfig` to `{ MODE: 3 }`, to test that everything really is migrated.
9. Remove the global `Vue.use( i18n )` calls, and change the i18n plugin to use the Vue 3 API (doing this earlier would make the i18n plugin unavailable to code using the Vue 2 mounting API).
10. Switch the Vue build in MediaWiki from `@vue/compat` to `vue`. We're now running real Vue 3, without any compat behavior.
11. At our leisure, remove `compatConfig` from everything.
(*) Version 3.2 is needed because it fixes [[https://github.com/vuejs/vue-next/issues/3944|a bug]] that breaks the i18n plugin