しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Vue.js 3.0 へのアップグレードを行った作業メモ

これまでも小規模なアプリで Vue.js 2.x を使っていましたが、そろそろ 3.0 へアップグレードするかという気持ちになったので、何故か日付が変わってから作業しました。

該当のコミット、Pull Request は以下のようになりました。一部分だけサラッと使えるので割と好きです。

特に Vue.js 周りの情報は追っていなかった結果として多少はまったので、自分が今後はまらないためにメモを残しておきます。マイグレーション手順とか言う大げさなものではない単なるメモです。

必要なライブラリが分かりにくい

Vue CLI や TypeScript とかを使うまでもない規模なため、これまでも cdnjs で公開されているライブラリを参照して使っていましたが、Vue.js 3.0 になって種類が増えていたので混乱しました。

正直なところファイル名からは推測できなかったので、適当にそれっぽいものを選んだら案の定動きませんでした。ちゃんとドキュメントを確認して必要なライブラリを探すことにしました。

結論としては素の HTML でサラッと使う場合は vue.global.prod.js を選べばよいらしいです。最初に選んだ runtime と付いているものは、テンプレートがコンパイル済みの場合に使うものだったようです。

最近のブラウザだと ES Modules が使えるらしいので、esm-browser が付いたものを選んでモジュールベースで書くことも出来るようですが、そこまでは試しませんでした。

Vue コンストラクタが消滅

とりあえずライブラリだけ 3.0 に入れ替えて試したらコンストラクタが無いというエラーになりました。Vue 2.x では new Vue({...}) をテンプレのように書いてきましたが、この辺りが大きく変わったようです。

アプリの定義周りは特に変わっていないみたいですが、これまでのコンストラクタに相当していた処理は Vue.createAppmount の二つに分離されたようです。

// これまでの書き方
//const app = new Vue({
//  el: '#app'
//  data: {
//    title: 'initial title',
//    body: 'initial body'
//  }
//});

// これからの書き方
const app = {
  data() {
    return {
      title: 'initial title',
      body: 'initial body'
    };
  }
};

Vue.createApp(app).mount("#app");

これに関しては 2.x がオブジェクト作っただけで動いていたのがそもそも気持ち悪かったので、個人的には良い改善だと思いました。副作用バリバリのコンストラクタは好きではありません。

this.$set が消滅

上の修正でアプリケーションは動き始めましたが、次は API から取ってきたデータを View に反映させるために、プロパティに値をセットする部分でエラーになりました。

エラーメッセージを確認すると $set というメソッドは無いと言われていました。

これまでは $set 経由じゃないと変更通知が動かなかったですが、このメソッドが消滅したということはプロパティに直接渡しても良い気がしたので試したら動きました。

正直なところ Knockout.js の Observable の getter / setter よりも酷いデザインだと思っていたので、通常のプロパティと同じように使えるようになったのは便利でした。

// これまでの書き方
//this.$set(this, 'title', 'hello');
//this.$set(this, 'body', 'world');

// これからの書き方
this.title = 'hello';
this.body = 'world';

仕組みは良く分かっていないですが、ドキュメントをチラ見した感じでは内部的に Proxy クラスを自動的に作っていろいろと頑張っているようです。

C#er 的に言うと Entity Framework Core の Lazy Loading Proxy と同じ仕組みっぽさがあります。

Vue.filter が消滅

事前に Vue.js プロフェッショナルの @nahokomatsui から Vue.js 3.0 では Filters が使えなくなると聞いていたので、実行時エラーを出す前にメソッドを呼び出す方向で対応を行いました。

公式ドキュメントにマイグレーション方法が書いてありましたが、異常なほどコードが美しくないと思ったので、この方向での対応は止めておきました。

Vue.js 3.0 の紹介をしている記事をいくつか読んでみましたが、Filters はメソッドを使って対応できるとだけ書いてあって、そのメソッドが何を指しているのか若干不明瞭でした。

結局のところは methods に追加した関数が呼び出せるという話だったようです。

個人的にはパイプでフィルタリングを行う Vue.js 2.x までのコードが結構好きでした。

メソッド呼び出しだと複数のフィルタを実行する場合に括弧が増えて分かりにくいですが、そういった場合には computed を使えということなのでしょう。

<!-- これまでの書き方 -->
<!--
<div id="app">
  <h1>{{ title | toUpperCase }}</h1>
  <p>{{ body }}</p>
</div>

<script>
Vue.filter('toUpperCase', function (value) {
  return value.toUpperCase();
});

const app = new Vue({
  el: '#app',
  data: {
    title: 'initial title',
    body: 'initial body'
  }
});
</script>
-->

<!-- これからの書き方 -->
<div id="app">
  <h1>{{ toUpperCase(title) }}</h1>
  <p>{{ body }}</p>
</div>

<script>
const app = {
  data() {
    return {
      title: 'initial title',
      body: 'initial body'
    };
  },
  methods: {
    toUpperCase(value) {
      return value.toUpperCase();
    }
  }
};

Vue.createApp(app).mount("#app");
</script>

ここまでの作業で自分の利用範囲では 3.0 への移行が完了しました。2.x ではイマイチだと思っていた部分がキッチリ改善されているのは良いですね。今後もカジュアルに使えるライブラリであってほしいです。

パフォーマンスが良くなっているという噂も聞きましたが、自分の利用範囲では関係なかったです。