If you have any thoughts on my blog or articles and you want to let me know, you can either post a comment below(public) or tell me via this i_kkkp@163.com

Approach for Coexistence Development of Vue 2/3

Introduction

After December 31, 2023, the functionality of Vue 2 will still be available, but no further updates will be provided, including security updates and browser compatibility.

Evan announced that the first RC of Vue 3 will be released in mid-July. This article suggests that library/plugin authors start migrating their support to Vue 3. However, due to significant changes in API and behavior, is it possible to make our libraries support both Vue 2 and 3 simultaneously?

Universal Code

The simplest approach is to write universal code that works for both versions, without any additional modifications, similar to what people have done with Python 2 and 3. However, simplicity does not mean it’s easy. Writing such components requires avoiding newly introduced features in Vue 3 and deprecated features in Vue 2. In other words, you cannot use:

  • Composition API
  • .sync and .native modifiers
  • Filters
  • 3rd-party vendor objects

Using Branches

The response from core team members suggests using different branches to separate support for each targeted version. This is a good solution for existing and mature libraries, as their codebases are usually more stable, and version targeting optimizations may require better code isolation.

The downside of this approach is that you need to maintain two codebases, which doubles your workload. It is not ideal for small libraries or new libraries that want to support both versions and avoid duplicating bug fixes or feature additions. I do not recommend using this approach from the beginning of a project.

Build Scripts

In VueUse, I wrote some build scripts to import code from the target version’s API during the build. After that, I need to publish two tags, vue2 and vue3, to differentiate the support for different versions. With this, I can write the code once and make the library support both Vue versions. The issue is that I need to build twice on each version and guide users to install the corresponding plugin versions (manually install @vue/composition-api for Vue 2).

Approach for Coexistence Development of Vue 2/3

Simultaneous Support for Vue 2/3 Projects

Possible Scenarios for Vue 2/3 Projects

Progressive Migration: If there is a large Vue 2 project but you want to gradually migrate to Vue 3, you can choose to introduce Vue 3 into the project and then gradually migrate Vue 2 components to Vue 3.

Compatibility with Dependency Libraries and Plugins: If the project depends on some Vue 2 plugins or libraries that have not been upgraded to Vue 3 yet, it may be necessary to use both Vue 2 and Vue 3 to ensure compatibility.

Adopting Vue 3 for New Features: You may want to use Vue 3 in the project to take advantage of its new features and performance benefits while still keeping Vue 2 for old components or features.

Project Integration: Based on experience requirements within the company, there is a need to present Vue 2/3 projects on the same page.

Internal Component Asset Maintainer: Support is needed for both Vue 2 and 3 projects, and the capabilities must be consistent.

Legacy Application Developer: Need to use a third-party chart component, but only the Vue 3 version is available, not the Vue 2 version.

Solutions

1. Coexistence of Vue 2/3 Projects

vue-5

Directly create a Vue 3 root instance using createApp and mount it to the Vue 2 root instance using the mount method. This allows using Vue 3 components in a Vue 2 project.

The related code repository can be found here: vue5

vue-5

// Vue 3 项⽬
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#vue3')
// Vue 2 项⽬
import Vue from 'vue2'
import App from './App.vue'

new Vue({
  render: h => h(App as any),
}).$mount('#vue2')

The important thing about this approach is that we use the vite.config.ts to solve the compilation problem of different modules: I wrote some build scripts to import code from the target version’s API during the build. After that, I need to publish two tags vue2 and vue3 to differentiate the support for different versions. But the issue is that it requires guiding users to install the corresponding plugin versions on each version. This is not very friendly for developers to deal with package conflicts.

import path from 'path'
import { defineConfig } from 'vite'
import Vue2 from '@vitejs/plugin-vue2'
import Vue3 from '@vitejs/plugin-vue'
import Inspect from 'vite-plugin-inspect'
import compiler from 'vue2/compiler-sfc'

const src = path.resolve(__dirname, 'src')

export default defineConfig({
  plugins: [
    Vue3({
      include: [/vue3[/\\].*\.vue$/],
    }),
    Vue2({
      include: [/vue2[/\\].*\.vue$/],
      compiler: compiler as any,
    }),
    Inspect(),
  ],
})

Here we have separated Vue 2 and Vue 3 into two independent packages and configured different compilation rules in vite.config.ts, allowing us to use Vue 2 and Vue 3 in the same page.

2. JessicaSachs/petite Solution

Let’s briefly introduce petite:

Petite is a subjective GitHub template built for Vue component authors. It sets up the tools needed for developing, documenting, and testing common SFC components and is backward compatible with Vue 2.7 runtime.

This is achieved through some runtime helper functions and a very opinionated monolithic library structure.

Petite sets up Vite, Volar, Linting, Vitepress, TypeScript, and Testing, allowing you to choose to write Vue 3-style code while easily maintaining backward compatibility with Vue 2.x users.

This also means that you will be publishing two versions of your package on npm instead of breaking major versions to support Vue 2 or Vue 3.

The downside of this approach is that your users will need to install the new version when upgrading and changing imports. The benefit is that you can write backward-compatible code more easily and provide regular updates for users. Additionally, you can also separate dependencies for Vue 2-only and Vue 3-only.

If you use lodash in your shared code, you will need to run pnpm build in the workspace root directory, and each package (lib-vue3, lib-vue2) should be deployed independently.

3. vue-bridge Solution

4. vue-demi Solution

Repository example: vue-demi

Vue Demi is a development utility that allows you to write universal Vue libraries for both Vue 2 and 3 without worrying about the version installed by the user.

When creating a Vue plugin/library, simply install vue-demi as a dependency and import any Vue-related content from it. Publish your plugin/library as usual, and your package will become universal!

{
  "dependencies": {
    "vue-demi": "latest"
  }
}
import Vue, { reactive, ref } from 'vue-demi'

At the underlying level, it uses the postinstall npm hook. After installing all the packages, the script will start checking the installed Vue version and redirect the exports based on the local Vue version. When using Vue 2, it will also automatically install @vue/composition-api if it is not already installed.

Points to note about libraries/components:

Libraries/Components

  • Single repository - Multiple package builds
  • Dependency management
  • Alias configuration
    • NPM package names
    • Build tool configuration

Importing Vue 3 Components in a Vue 2 Application

There are limitations to component interoperability

  • Shared context
  • Scoped slots
  • Events

Approach for importing Vue 3 components in a Vue 2 application

  • Vue 3 can have multiple global instances
  • Prerequisite: Upgrade Vue 2 to 2.7, remove outdated plugins from Vue CLI
  • Interoperability layer: Custom Elements
  • Build tool: Vite
Vue Components and Web Components Custom Elements 2023-11-24-随笔

Comments