Closed25

[キャッチアップ] vue-demi

shingo.sasakishingo.sasaki

最初に世界観を以下ブログで確認。

https://antfu.me/posts/make-libraries-working-with-vue-2-and-3

  • Vue 3 がリリースされるし、ライブラリやプラグインの開発者はサポートの準備したほうがいいよね。でも破壊的変更とか振る舞いの変更がいっぱいあるから、両方を同時にサポートできるもんなのかな?
  • 一番シンプルな方法は、Python みたいに、両バージョンで動くコードを書くことだよね。でもそのためには、Composition API みたいな Vue 3 でしか動かないコードを使うのを諦めるしかなくて、それは Vue 3 の恩恵が受けられなくてツライよね
  • この問題に対するコアチームの回答として、ブランチを分けてバージョンごとに開発を続けることが上げられてるよ。これはメジャーで成熟したライブラリなら良い案だけど、これから開発するプロジェクトや小さなプロジェクトに置いては、機能開発やバグ修正が二重になってツライよね
  • VueUse を開発する中では、使用する API を Vue2/Vue3 切り替えられるビルドスクリプトを用意して、両バージョンに対してリリースを行う手法をとってきたけど、二度手間だしユーザーに Composition API プラグインの導入促さないといけないしで大変だよ
  • Vue Demi はそんな課題から生み出されたもので、パッケージをインストールした環境にある Vue のバージョンを見て、 API のバージョンを自動で切り替えたり Composition API プラグインを自動インストールする仕組みを備えたよ
shingo.sasakishingo.sasaki

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 の結果には影響しない)

shingo.sasakishingo.sasaki

パッケージ内のコードのうち、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";
shingo.sasakishingo.sasaki

Vite を使用している場合はもうひと手間あるようだけど、今回は vue-cli に頼っているのでこれで完了。

shingo.sasakishingo.sasaki

これだけで完了とか本当にござるか〜〜〜??

テストを実行してみる。

ちなみに今回実験してるパッケージでは、対象コンポーネントを使った簡易アプリを 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)

通ったわ。

shingo.sasakishingo.sasaki

開発環境の 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
shingo.sasakishingo.sasaki

開発ビルド時に警告が出て

 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)
shingo.sasakishingo.sasaki

よく見ると 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.

shingo.sasakishingo.sasaki

本当に Vue のバージョンが切り替わってるか怪しいものなので検証してみる。

以下みたいな Vue のバージョンを判定する関数が提供されるので、この結果を確認してみる

import { isVue2, isVue3 } from 'vue-demi'
setup() {
    setup(props, { emit }) {
    if (isVue2) {
      alert("Vue 2!!");
    }
    if (isVue3) {
      alert("Vue 3!!");
    }
}
shingo.sasakishingo.sasaki

あれ、 Vue 2 になってくれない。
というかエラーが再発してる。

これは npx vue-demi-fix で修正できたんじゃなくて、単に Vue 3 に戻っただけ臭いな。

shingo.sasakishingo.sasaki

うーん、よくわからないけど、とりあえず Vue 3 では引き続き正常に動いてるんだし、このままリリースしちゃって、 Vue 2 から使おうとするとどうなるか見てみようかなぁ。

shingo.sasakishingo.sasaki

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)
shingo.sasakishingo.sasaki

ローカルパッケージの追加がだめな可能性もあるので、一度 Vue3 プロジェクトの方でもやってみたけどダメだったので、ローカルパッケージの使い方がダメ臭いな。

shingo.sasakishingo.sasaki

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)
shingo.sasakishingo.sasaki

普通にリリース版のパッケージを利用しつつ、 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

ダメだ。まぁこれは予定ない。

shingo.sasakishingo.sasaki

うーん、 Vue3 で動くことは間違いないし、やっぱり1回リリースしちゃうか。

shingo.sasakishingo.sasaki

ダメっぽい。
やっぱ Vue3 特有の構文使っちゃってるとダメなのかな。

shingo.sasakishingo.sasaki

といっても composition API ぐらいしかそれらしいのなくて、プラグインで互換性がないような使い方もしてないし謎。

単純な見落としに過ぎない気はするけど、こうも出鼻をくじかれると使う気がなくなってしまうので一旦諦め。

このスクラップは2022/01/30にクローズされました