Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

Vue3に書き直してみる

Vue2からVue3へ書き直す際に情報が不足していて困ったのでまとめておきます。

公式ドキュメント

まだversion3の公式ドキュメントは揃っていません。マージされたPRRFCを見るしかない状況です。

migrate from v2 to v3

※「これだけ押さえれば7割くらいは移行できるだろう」くらいで網羅的には書けておりません

  1. new Vueによるインスタンス生成はcreateApp()へと置き換え
  2. setupメソッドの引数の型推論をしたい場合はSFCのexportするオブジェクトをdefineComponentでラップ
  3. new Vueのdataやmethodsはinject()app.provide()を使ってグローバルステートで置き換え React Context APIのようなもので、書き方もだいたい同じ。
  4. watchやdata、computed、mountedなどほとんどのプロパティは消えてsetupへ集約
  5. vue-template-compilerの代わりに@vue/compiler-sfcを使う
  6. shims-vue.d.ts(SFCの型定義ファイル)の書き方が変わる
  7. render関数の引数として渡されていたh関数はグローバルインポート

インストール

バージョンは2020年6月19日現在のものです。

  • vue@3.0.0-beta.15

SFCで書きたい、webpackを使いたいのであれば以下も必要です。

  • vue-loader@16.0.0
  • @vue/compiler-sfc@3.0.0-beta.15
  • webpack, webpack-cli

書き方

大きく異なる点がいくつかあります

  • composition APIが使える
  • webpackのalias設定がいらなくなる
  • new Vueが消える
  • Vueオブジェクトに生えていたAPIがnamed exportsオンリーになる
  • TypeScriptとの親和性が高くなる

composition APIが使える

まずこれがかなり大きいです。このお陰でVueへもTypeScriptを導入しやすくなりました。this関連でひどい目に合わずに済むのは大変嬉しいです。

また、書き方もかなり楽になりました。ロジックを外へ抽出できるようになった点も嬉しいです。ReactのHooksとできることはほぼ同じですが、かなりVueらしいやり方に収まった感覚があり、とても好きです。

webpackのalias設定がいらなくなる

new Vueの代わりにcreateAppというメソッドを使ってマウントするようになりました。そのため、vue/dist/vue.esm.jsへエイリアスを張らなくて良くなります。逆に、Vue2の頃のaliasがwebpack.config.jsに残っているとビルドが失敗します。

createApp

こいつが結構やっかいです。従来のやりかたと所々異なっています。

rfcs/0009-global-api-change.md at 9f18645a700f54e7d9a4e3b53046d48791e31459 · vuejs/rfcs · GitHub

上記リンク先を見ていただけばわかりますが、ほとんどは同じです。しかし、たとえば下のようなケースでは若干変わってきます。

// v2
new Vue({
  render: (h) =>
    h(MyComponent, {
      props: { prop1, prop2 },
    })
})
// v3
const app = createApp(MyComponent, { prop1, prop2 });

僕はかなり泥沼にハマりました。

defineComponentについて

setupのpropで型推論を働かせたいときにはdefineComponentでラップする必要があります。逆に、setupでpropを参照しないのならdefineComponentで囲む必要はなく、従来のSFCの書き方で十分です。

公式にも書いてありますが、defineComponent自体は受け取ったオブジェクトをただ返すだけの関数で、型推論以外では1ミリも役に立ちません。

Note that implementation-wise defineComponent does nothing - it simply returns the object passed to it. However, in terms of typing, the returned value has a synthetic type of a constructor for manual render function, TSX and IDE tooling support. This mismatch is an intentional trade-off.

JSXについて

renderのなかでJSXを使えるようになりました(今まではh関数を使ったcreateElement的な書き方しかできなかった)。これでvueファイルの中の<template>を消してJSオンリーでかけるようになりました。

Global APIのnamed exports化

tree-shakingのための変更です。Vue以下に生えていたVue.nextTickなどはnextTick関数としてexportされるようになります。Vue.nextTickは使えなくなります。このように、Vue3には後方互換性のない変更があるので注意が必要です。

SFCの型定義が変わる

new Vueによるインスタンス生成から変わった影響で、SFCの型定義もdefineComponentを使うものへ変わりました。

declare module "*.vue" {
  import { defineComponent } from "vue";
  const component: ReturnType<typeof defineComponent>;
  export default component;
}

従来の型定義はcreateAppの引数として使えないので注意してください。

参考: vue-next(Vue.js 3.0 wip)+ TypeScript + webpackでの開発環境を構築する - Qiita