With vue-next now a release candidate, many library authors have begun porting their vue 2.x library to vue-next.
There are solutions to enable library compatibility with both Vue 2 and Vue 3. For example, vue-demi allows your library to access the appropriate Vue APIs for the version of vue
you have installed installed.
However, there are no current solutions for testing your library against both Vue 2 and Vue 3. This is particularly relevant to me as I maintain vue-composable, a library that has both vue 2.x and vue-next support -- and of course, a suite of automated tests.
Preface
First, an important note: this may not be the most correct or the cleanest approach, but it works and has been tested for quite a few months now.
Probably the best place for this to be used is with the composition-api libraries, because the render has breaking changes, that make this method to be a bit messy.
Introduction
I will cover how to setup a project to work with both vue-next and vue 2 + composition-api. It will target vue-next primarily.
I've tried to keep the example as simple as possible, but some prior knowledge is required:
Vue 3
-
Vue 2.x
+@vue/composition-api
Jest
Project
For the project we will create a composable
called useApi
import { ref } from "vue";
export function useApi(factory, handleResponse) {
const isLoading = ref(false);
const result = ref(null);
const error = ref(null);
const execute = async (...args) => {
const request = factory(...args);
isLoading.value = true;
error.value = null;
try {
const response = await fetch(request);
const valueResponse = await handleResponse(response);
result.value = valueResponse;
return valueResponse;
} catch (e) {
error.value = e;
result.value = null;
} finally {
isLoading.value = false;
}
};
return {
isLoading,
result,
error,
execute,
};
}
As written, this would work with vue-next, but not with vue 2.x.
api.js
So we will need to replace the import { ref } from 'vue'
with our own import { ref } from './api'
.
// api.js [Vue3]
export { ref } from "vue";
In api.js
we will expose all the required Vue APIs needed in the project. All vue
dependencies should come from this file. (Otherwise we might end up importing different vue
versions and breaking reactivity
.)
vue 2 + composition-api
Now we will create a new file called api.2.js
export { ref } from "@vue/composition-api";
We can hot-swap in this file when testing.
Test
Now we just need to create a test __tests__/index.spec.js
import { useApi } from "../index";
import { mockFetch } from "./utils";
describe("useApi", () => {
it("should work", async () => {
mockFetch({ name: "Luke Skywalker" });
const userList = useApi(
(page) => ({ url: `https://swapi.dev/api/people/${page}` }),
(r) => r.json()
);
await userList.execute(1);
expect(userList.result.value.name).toBe("Luke Skywalker");
});
});
Now we could just change the import { ref } from './api'
to import { ref } from './api.2'
. Only one problem remains: @vue/composition-api needs to be initialised by running Vue.use(VueCompositionApi)
.
Jest config
So we will add a jest.config.js
and create a file (__tests__/setupTest.js
) where we can initialise the Vue 2 composition-api
plugin.
Because we currently only need the setupTest.js
when using vue 2.x we will pass an environment variable named VUE
with the version number. If is 2
we will include
// jest.config.js
const isVue2 = process.env.VUE === "2";
module.exports = {
setupFiles: isVue2 ? ["<rootDir>/__tests__/setupTest.2.js"] : undefined,
testMatch: ["<rootDir>/__tests__/**/*spec.[jt]s?(x)"],
};
For the setupTest.2.js
we will need to import vue
, but we can't just import it normally because we already have vue@next
in our package dependencies. So we will need to add vue 2.x
with a different name (vue2
).
// package.json
"dependencies": {
"@vue/composition-api": "^1.0.0-beta.6",
"vue": "^3.0.0-rc.4",
"vue2": "npm:vue@^2.6.1"
}
Now we can import it in setupTest.2.js
import Vue from "vue2";
import VueCompositionApi from "@vue/composition-api";
Vue.use(VueCompositionApi);
Great. However, we're not done yet. If you pass env.VUE === '2'
, it may initialize the composition api, but the tests would still run using vue-next 🤔
To make sure we run with the correct API we will mock the api.js
module with jest.
// setupTest.2.js
// mock for the correct api
jest.mock("./../api", () => jest.requireActual("./../api.2"));
//... importing vue and use CompositionApi
Now if you pass the VUE=2
environemnt variable it will use @vue/composition-api instead of vue-next.
Scripts
You can have yarn test
test both versions by doing
// package.json
"scripts": {
"test": "yarn test:vue2 && yarn test:vue3",
"test:vue2": "VUE=2 yarn jest",
"test:vue3": "yarn jest"
},
Multi-platform
You can install cross-env
and run both
yarn add cross-env -D
//package.json
"scripts": {
"test": "yarn test:vue2 && yarn test:vue3",
"test:vue2": "cross-env VUE=2 yarn jest",
"test:vue3": "yarn jest"
},
Pitfalls
If there's file that imports vue
or @vue/composition-api
, most likely it will compile but it will not work properly, reactivity will not work, etc. Please make sure you only import from api.js
.
Typescript
types might be different and have some nuances, if using with typescript
please export the types from api.ts
, if the types don't exist in v2
or v3
, export custom types using api.ts
or api.2.ts
.
Conclusion
Hopefully this will give you a good start to begin experimenting with both libraries within the same code base. There are a few missing apis in @vue/composition-api, and this approach will allow you to implement some of them (example)
Please check up-to-date repo for the all the code and working project.
Big thanks to @danielroe for reviewing this article, so many awesome changes 🎉
Top comments (2)
This is great and just what I need to do with vue-concurrency.
I am missing one thing though: this just solves testing, but the importing from
./api
would effectively make the lib possible to use only for Vue 3 right? So there's some more hacking needed to make the library usable both with Vue 2 and Vue 3.To answer my own question: vue-composable renames the file as needed during build.
github.com/pikax/vue-composable/bl...