[キャッチアップ] vue-demi
最初に世界観を以下ブログで確認。
- Vue 3 がリリースされるし、ライブラリやプラグインの開発者はサポートの準備したほうがいいよね。でも破壊的変更とか振る舞いの変更がいっぱいあるから、両方を同時にサポートできるもんなのかな?
- 一番シンプルな方法は、Python みたいに、両バージョンで動くコードを書くことだよね。でもそのためには、Composition API みたいな Vue 3 でしか動かないコードを使うのを諦めるしかなくて、それは Vue 3 の恩恵が受けられなくてツライよね
- この問題に対するコアチームの回答として、ブランチを分けてバージョンごとに開発を続けることが上げられてるよ。これはメジャーで成熟したライブラリなら良い案だけど、これから開発するプロジェクトや小さなプロジェクトに置いては、機能開発やバグ修正が二重になってツライよね
-
VueUse
を開発する中では、使用する API を Vue2/Vue3 切り替えられるビルドスクリプトを用意して、両バージョンに対してリリースを行う手法をとってきたけど、二度手間だしユーザーに Composition API プラグインの導入促さないといけないしで大変だよ - Vue Demi はそんな課題から生み出されたもので、パッケージをインストールした環境にある Vue のバージョンを見て、 API のバージョンを自動で切り替えたり Composition API プラグインを自動インストールする仕組みを備えたよ
Vue 3 向けに作った以下パッケージを Vue 2 にも提供できないかを検証していく
まず README に沿って改修していく
まず v-demi 自体を devDependencies に追加
$ yarn add -D vue-demi
peerDependencies には CompositionAPI プラグインと、Vue 2,3 それぞれのバージョンを記述。
"peerDependencies": {
"@vue/composition-api": "^1.0.0",
"vue": "^2.0.0 || >=3.0.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
},
これがパッケージの利用側は Vue のバージョンをと合わないけど、compositionAPI プラグインが
必要な場合もあるよという案内になる。(yarn install
の結果には影響しない)
パッケージ内のコードのうち、vue
から インポートしている箇所を全て vue-demi
に差し替える。
-import { reactive } from "vue";
+import { reactive } from "vue-demi";
-import { createApp } from "vue";
+import { createApp } from "vue-demi";
-import { defineComponent, nextTick, PropType, reactive, ref, watch } from "vue";
+import { defineComponent, nextTick, PropType, reactive, ref, watch } from "vue-demi";
Vite を使用している場合はもうひと手間あるようだけど、今回は vue-cli に頼っているのでこれで完了。
これだけで完了とか本当にござるか〜〜〜??
テストを実行してみる。
ちなみに今回実験してるパッケージでは、対象コンポーネントを使った簡易アプリを Cypress でテストしているため E2E テストのみ用意している。
vue-3-better-picker
✓ reflects the default text props (1172ms)
✓ reflects the specified text props (1338ms)
✓ reflects the default selection prop (612ms)
✓ reflects the specified selection prop (536ms)
✓ reflects the updated data on props (1097ms)
✓ emits the change event when the scrolling ends (1051ms)
✓ emits the select event when clicked confirm button (841ms)
✓ emits no event when clicked cancel button (936ms)
✓ emits no event when clicked outside picker (743ms)
通ったわ。
開発環境の Vue のバージョンを切り替える
$ npx vue-demi-switch 2
[vue-demi] Composition API plugin is not found. Please run "npm install @vue/composition-api" to install.
[vue-demi] Switched for Vue 2 (entry: "vue")
CompositionAPI プラグインが必要になるのでインストールしとく
$ yarn add -D
@vue/composition-api
開発ビルド時に警告が出て
WARNING Compiled with 2 warnings 1:08:16
warning in ./node_modules/vue-demi/lib/index.mjs
"export 'default' (imported as 'Vue') was not found in 'vue'
warning in ./node_modules/vue-demi/lib/index.mjs
"export 'default' (reexported as 'Vue') was not found in 'vue'
コンソールエラーも出て動かなくなっちゃった
index.mjs?8afd:15 Uncaught TypeError: Cannot read properties of undefined (reading 'version')
at eval (index.mjs?8afd:15:1)
at Module../node_modules/vue-demi/lib/index.mjs (chunk-vendors.js:296:1)
at __webpack_require__ (app.js:849:30)
at fn (app.js:151:20)
at eval (main.ts?4068:1:1)
at Module../dev/src/main.ts (app.js:986:1)
at __webpack_require__ (app.js:849:30)
at fn (app.js:151:20)
at Object.1 (app.js:1162:18)
at __webpack_require__ (app.js:849:30)
同じエラーの Issue があったけど相手にされてなかった。となるとあるあるなエラーじゃないのか。
警告の方も似たような Issue あり
npx vue-demi-fix
とかいう魔法みたいなコマンドが出てきたので実行してみる。
なおった。なんだこれ。
よく見ると README にちゃんと書いてあった。
If the postinstall hook doesn't get triggered or you have updated the Vue version, try to run the following command to resolve the redirecting.
本当に Vue のバージョンが切り替わってるか怪しいものなので検証してみる。
以下みたいな Vue のバージョンを判定する関数が提供されるので、この結果を確認してみる
import { isVue2, isVue3 } from 'vue-demi'
setup() {
setup(props, { emit }) {
if (isVue2) {
alert("Vue 2!!");
}
if (isVue3) {
alert("Vue 3!!");
}
}
あれ、 Vue 2 になってくれない。
というかエラーが再発してる。
これは npx vue-demi-fix
で修正できたんじゃなくて、単に Vue 3 に戻っただけ臭いな。
うーん、よくわからないけど、とりあえず Vue 3 では引き続き正常に動いてるんだし、このままリリースしちゃって、 Vue 2 から使おうとするとどうなるか見てみようかなぁ。
別にリリースせずともこういうやり方すればパッケージ利用側の動作確認できるのかな。
Vue2 プロジェクトを新たに作って、相対パスでパッケージをインポートするのをやってみる。
$ yarn add ../vue-better-picker
ちゃんと対象パッケージのビルド成果物が node_modules に入っていることを確認
$ ls node_modules/vue-3-better-picker/dist/
demo.html vue-3-better-picker.common.js.map vue-3-better-picker.umd.js.map vue-3-better-picker.umd.min.js.map
vue-3-better-picker.common.js vue-3-better-picker.umd.js vue-3-better-picker.umd.min.js
Vue2 なのでプラグインも追加する
$ yarn add -D @vue/composition-api
さぁ、Vue2 のプロジェクトでビルドできるのか。
が、ダメ!
vue-3-better-picker.umd.min.js?de62:1 Uncaught TypeError: Object(...) is not a function
at a (vue-3-better-picker.umd.min.js?de62:1:1)
at Module.fb15 (vue-3-better-picker.umd.min.js?de62:1:1)
at o (vue-3-better-picker.umd.min.js?de62:1:1)
at 052c (vue-3-better-picker.umd.min.js?de62:1:1)
at eval (vue-3-better-picker.umd.min.js?de62:1:1)
at eval (vue-3-better-picker.umd.min.js?de62:1:1)
at eval (vue-3-better-picker.umd.min.js?de62:1:1)
at Object../node_modules/vue-3-better-picker/dist/vue-3-better-picker.umd.min.js (chunk-vendors.js:1418:1)
at __webpack_require__ (app.js:849:30)
at fn (app.js:151:20)
ローカルパッケージの追加がだめな可能性もあるので、一度 Vue3 プロジェクトの方でもやってみたけどダメだったので、ローカルパッケージの使い方がダメ臭いな。
vue-demi の問題なのかを切り分けるために、vue-demi を使用していないリリースバージョンでビルドしなおして同じ動作を確認する。'
これもダメ。vue-demi 以前の話だったみたいだけど何が悪いんだろ。
runtime-core.esm-bundler.js:1985 Uncaught TypeError: Cannot read properties of null (reading 'subTree')
at Proxy.<anonymous> (runtime-core.esm-bundler.js:1985:39)
at renderComponentRoot (runtime-core.esm-bundler.js:893:44)
at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js:5029:57)
at ReactiveEffect.run (reactivity.esm-bundler.js:160:29)
at setupRenderEffect (runtime-core.esm-bundler.js:5155:9)
at mountComponent (runtime-core.esm-bundler.js:4938:9)
at processComponent (runtime-core.esm-bundler.js:4896:17)
at patch (runtime-core.esm-bundler.js:4488:21)
at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js:5036:21)
at ReactiveEffect.run (reactivity.esm-bundler.js:160:29)
普通にリリース版のパッケージを利用しつつ、 dist/
内の成果物だけコピーして差し替えるという手法を取る。これは vue-demi の仕組み上上手く動かない気がする。
$ cp dist/vue-3-better-picker.umd.min.js ../vue3-sandbox/node_modules/vue-3-better-picker/dist/vue-3-better-picker.umd.min.js
Vue3 では動く。
Vue2 はどうだろ。
$ cp dist/vue-3-better-picker.umd.min.js ../vue-2-sandbox/node_modules/vue-3-better-picker/dist/vue-3-better-picker.umd.min.js
ダメだ。まぁこれは予定ない。
うーん、 Vue3 で動くことは間違いないし、やっぱり1回リリースしちゃうか。
v1.1.0 を出してみる。
ダメっぽい。
やっぱ Vue3 特有の構文使っちゃってるとダメなのかな。
といっても composition API ぐらいしかそれらしいのなくて、プラグインで互換性がないような使い方もしてないし謎。
単純な見落としに過ぎない気はするけど、こうも出鼻をくじかれると使う気がなくなってしまうので一旦諦め。