キーポイント
- Vue 3 recently released with plenty of new APIs that address pain points observed when using Vue at scale.
- The new composition API allows developer to package and reuse custom bits of logic across components. The composition API is entirely opt-in and does not replace the current options API.
- Vue 3's custom renderer API enables using Vue in non-DOM contexts (e.g., native, mobile, webGL).
- New Suspense, Teleport built-in components and CSS scoping rules add expressivity to Vue's template language.
- New fragment and custom events API let Vue components have a public API closer to that of regular DOM elements.
Vue 3コアは昨年9月にリリースされました。Vue 3は、新しいコアAPI、パフォーマンスの向上、およびTypeScriptサポートの改善をもたらします。新しいAPIには、コンポジションAPI、カスタムイベントAPI、カスタムレンダラAPI、サスペンスコンポーネント、テレポート、フラグメント、新しいCSSスコープルールが含まれます。Vueエコシステムの主要物 (devtool、router、CLI、vue-test-utils、Vuexなど) は、Vue 3への移行を完了しています。
コンポジションAPI
Vue Composition APIは、React HooksやDojo middlewareと同様の目標を達成します。ロジックの小片を関数にカプセル化し、再利用して構成できるため、コードサイズが小さくなり、パフォーマンスが向上する可能性があります。新しいコンポジションAPIは、開発者が関心事を単一のコンポジション関数 (Reactコンテキストのフックに類似) にグループ化できるようにすることで、保守性も支援します。さらに、コンポジションAPIには、TypeScriptでより適切な型推論を可能にするという副作用があります。Vueのドキュメントでは、大規模なコードベースで新しいコンポジションAPIを推奨しています:
Vueコンポーネントを作成すると、インターフェイスの繰り返し可能な部分とその機能を組み合わせて、再利用可能なコードに抽出できます。これだけでも、保守性と柔軟性の点でアプリケーションをかなり遠ざけることができます。ただし、私たちが収集してきた経験から、特にアプリケーションが非常に大きな場合は、これだけでは不十分である可能性があることが証明されています。数百のコンポーネントを考えてみてください。このような大規模なアプリケーションを扱う場合、コードの共有と再利用が特に重要になります。
小規模なコードベースでも、開発者は標準コンポーネントAPI (Vue 2で利用可能で、オプションAPIと呼ばれます) を継続するか、代わりに新しいコンポジションAPIの利点を活用できます。したがって、新しいコンポジションAPIは完全にオプトインされています。これにより、開発者は、フル機能のアプリケーションを開発する能力に影響を与えることなく、Vue 3の複雑さを学習するための進歩的なアプローチを採用できます。これは、プログレッシブJavaScriptフレームワークとしてのVueの自己記述に適合します。
コンポジションAPIは、コンポーネントに関連し、コンポーネントのスコープに配置されるエンティティを返すコンストラクターまたはファクトリとして機能する setup
関数に依存しています。setup ファクトリは、オプションAPI (例: mounted
) で使用可能なものをミラーリングするライフサイクルコールバック (例: onMounted
) を登録する場合があります。setup ファクトリは、コンポーネントの動作を指定するリアクティブ計算を定義することもできます。 — アトミックリアクティブ値を定義する ref
API、依存関係を定義して依存関係が更新されたときにエフェクトを実行する watch
および watchEffect
API、依存関係が更新されたときに純粋な計算を実行する computed
API。
setup ファクトリは、開発者が望む数のファクトリに分割でき、それらのファクトリから返された値がマージされて、最終的な setup 出力が形成されます。これらの各ファクトリは、Reactフックを彷彿とさせます。ただし、setup ファクトリは、コンポーネントの存続期間中に1回だけ実行されます。したがって、Vue 3は、複数回実行されているフックに起因する議論の余地のある厄介さや偶発的な複雑さ (Reactフックルール、eslintプラグイン、stale data 古いデータなど) なしで、Reactフックと同様の機能を実現します。
コンポジションAPIの使用方法のステップバイステップの例は、Vue 3のドキュメントにあります。
カスタムイベント
Vueコンポーネントは、属性 (props) 、イベントリスナ、および依存性注入 (dependency injection)を介してパラメーター化できます。カスタムイベントAPIを使用すると、開発者はVueコンポーネントのイベントインターフェイスを定義できます。Vue 3では、開発者が子コンポーネントのイベントインターフェイスを明示的に指定して、親コンポーネント上のこれらのイベントのリスナをバインドする必要があります。カスタムイベントAPIを使用すると、emits
プロパティを使用してコンポーネントが発行するイベントを宣言し、オプションでイベントをバリデーションする関数を提供できます:
app.component('custom-form', {
emits: {
// No validation
click: null,
// Validate submit event
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
},
methods: {
submitForm() {
this.$emit('submit', { email, password })
}
}
})
上で定義された <custom-form>
コンポーネントは、通常のVue構文 (<custom-form @submit="..."
) を使用して submit
イベント用にリスナをプロビジョニングできます。Vue 2.xと同様に、Vue 3カスタムイベントはバブルしません。Vue 3のドキュメントには、フォームでの使用例が記載されています。
Chris Fritz氏は、VueConfUSで、いわゆる透過ラッパーコンポーネントを作成するカスタムイベントの使用方法を示しました。Vueコンポーネントは、Webコンポーネントが許可する方法で通常の要素として推論されることはないでしょうが、Vue 3ではギャップを狭めることができます。
Fritz氏は、入力要素がどのようになっているのかを紹介しました:
<input v-model="searchText" placeholder="Search" @keyup.enter="search"/>
<BaseInput>
Vueコンポーネントにラップできます:
<BaseInput v-model="searchText" placeholder="Search" @keyup.enter="search"/>
以前のVue 2で可能だった方法よりはるかに簡単に:
<script>
export default { props: ['label'] }`
</script>
<template>
<label> {{ label }}
<input v-bind="$attrs" />
</label>
</template>
Fritz氏のプレゼンテーションは古いバージョンのVue 3を使用しており、BaseInput
コンポーネントの内部実装の詳細がいくつか変更されている可能性がありますが、コンポーネントの外部APIは同じままで、通常の <input>
要素で使用できるものを反映しています。
BaseInput
Vueコンポーネントの placeholder
属性とEnterキーイベントリスナは、v-bind="$attrs"
を使用して BaseInput
コンポーネントテンプレートの <input>
要素にバインドされます。label
属性は、BaseInput
コンポーネントの prop として明示的に宣言されているため、$attrs
オブジェクトには含まれません。placeholder
属性は、non-prop 属性と呼ばれます。すべての non-prop 属性は、$attrs
オブジェクトで使用できます。
カスタムレンダラ
カスタムレンダラAPI (createRenderer
メソッド) は、レンダリングメソッドをカスタマイズすることで、非DOMコンテキストでVueを使用できるようにします。このメソッドは、Vueコードベースで通常どおり使用できるレンダリング関数を返すファクトリです:
import { createRenderer } from 'vue'
const { render, createApp } = createRenderer<Node, Element>({
patchProp,
...nodeOps
})
APIは現在ほとんど文書化されておらず、非DOMプラットフォームをターゲットにしたいかなり高度なVueライブラリの作成者を対象としています。ネイティブプラットフォーム、canvas、pdfなどのReactレンダラを実装するために使用される react-reconciler パッケージ と同様の機能を提供します。Vueの作成者であるEvan You氏は、昨年4月の講演で、Vue 3のカスタムレンダラAPIのおかげでカスタムレンダラを実装するための継続的なユーザ主導の取り組みについて話し合いました — サーバサイドレンダリング用の @vue/compiler-ssr、モバイルプラットフォーム用のNativeScript/Vue統合 、端末用のvuminal、vuegl WebGLカスタムレンダラ。カスタムレンダラは、新しいVue 3テンプレートコンパイラと組み合わせて使用できます。新しいテンプレートコンパイラはソースマップとユーザ定義の変換プラグインを完全にサポートしているとVue Amsterdamで説明しました。コンパイラはさらに、Vue 3の上にさらに複雑なコンパイラを作成できるようにする階層化された設計に従います。
(vuminal アプリケーションの使用例 出典: vuminal’s GitHub repository)
フラグメント
Vueフラグメントは、マルチルートノードコンポーネントのサポートを追加します:
<custom-layout id="custom-layout" @click="changeValue">
</custom-layout>
// This will raise a warning
app.component('custom-layout', {
template: `
<header>...</header>
<main>...</main>
<footer>...</footer>
`
})
// No warnings, $attrs are passed to <main> element
app.component('custom-layout', {
template: `
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
`
})
custom-layout
コンポーネントのテンプレートには、3つのノードがあります。custom-layout
コンポーネントの props (idおよびクリックイベントハンドラ) は、v-bind="$attrs"
でアノテーションが付けられた main
コンポーネントにバインドされます。フラグメント属性を手動でバインドすると、フラグメントコンポーネントの non-props 属性をコンポーネントテンプレートの必要な場所に配布できます。前述のように、non-props 属性は、コンポーネントの明示的に提供された入力インターフェイスの一部ではない属性です。たとえば、コンポーネントの props プロパティで定義されていない属性や、emits プロパティで定義されていないイベントのリスナなどです。
テレポート
<Teleport>
s には、子コンポーネントを、親コンポーネントのDOM階層の外側に存在するDOMノードにレンダリングすることが含まれます したがって、テレポートは、モーダル、ドロップダウン、または通知に使用できます。
Vue 3のドキュメントには、モーダルを実装するためのAPIの使用例が次のとおりです:
app.component('modal-button', {
template: `
<button @click="modalOpen = true">
Open full screen modal! (With teleport!)
</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
`,
data() {
return {
modalOpen: false
}
}
})
<teleport>
組み込みコンポーネントは、モーダルのコンテンツをbody
タグ (<teleport to="body">
) の子としてレンダリングします。
サスペンス
<Suspense>
は、非同期コンポーネントで動作するように設計された新しいVue組み込み要素です。Vueのドキュメントは次のように説明しています:
非同期コンポーネントはデフォルトでサスペンド可能 (suspensible) です。 これは、親チェーンに
<Suspense>
がある場合、その<Suspense>
の非同期依存関係として扱われることを意味します。 この場合、ロード状態は<Suspense>
によって制御され、コンポーネント自体のロード、エラー、遅延、およびタイムアウトのオプションは無視されます。非同期コンポーネントは、
Suspense
制御をオプトアウトし、オプションでsuspensible: false
を指定することにより、コンポーネントが常に自身のロード状態を制御可能にできます。
Vue 3のリリースノートは、新しい Suspense コンポーネントはまだ実験的なものであり、安定したときにさらに文書化されると述べています:
また、現在文書化されていない
<Suspense>
コンポーネントを実装しました。これにより、ネストされた非同期依存関係 (非同期コンポーネント、またはasync setup()
を使用するコンポーネント) を初期レンダリングまたはブランチスイッチで待機できます。 Nuxt.jsチーム (Nuxt 3が進行中) でこの機能のテストとイテレーションを行っており、3.1で固まりそうです。
新しいCSSスコープルール
Vueシングルファイルコンポーネント (SFC) は、デフォルトでスコープが設定されているスタイルを定義します。SFCコンポーネントは、グローバルルールまたはスロット付きコンテンツのみを対象とするルールを指定できるようになりました。新機能は、 <style scoped>
構文を使用します。
<style scoped>
/* deep selectors */
::v-deep(.foo) {}
/* shorthand */
:deep(.foo) {}
/* targeting slot content */
::v-slotted(.foo) {}
/* shorthand */
:slotted(.foo) {}
/* one-off global rule */
::v-global(.foo) {}
/* shorthand */
:global(.foo) {}
</style>
新しく洗練されたスコープ機能は、CSS Scoping Module Level 1とCSS Shadow Partsの提案を動機付けたいくつかの一般的に発生するケースに対処するのに役立ちます。カスタム要素のシャドウDOMを使用すると、開発者は、詳細が、外部ページではなく、コンポーネント自体にのみ関連するマークアップのサブツリーにページを分割できます。CSS Shadow Parts仕様では、カスタム要素の分割に関連するトレードオフについて説明しています:
これにより、ページの一部を対象としたスタイルが誤って過剰に適用され、ページの別の部分が正しく表示されない可能性が低くなります。ただし、このスタイリングバリアにより、ページが実際に必要なコンポーネントと対話することも困難になります。
移行とエコシステム
開発者は、移行ガイドとともにVue 3のドキュメントをオンラインで確認できます。 Vueエコシステムの重要な部分はVue3への移行を終えており、いくつかはまだベータやRC段階にある可能性があります。Vueの公式ルータライブラリはv4をリリースし、Vue 3をサポートするようになりました。Vue開発者ツールはまだベータ版です。Vue 3を対象とする公式のテストスイートvue-test-utils v2もベータ版です。状態管理ライブラリVuexはリリース候補段階にあります。Vuex 4はVue 3と互換性があり、Vuex 3と同じAPIを提供します。
Vue 3のドキュメントは次のように警告しています:
Vue 2と互換性のある動作と互換性のない使用法のランタイム警告を備えたVue 3の専用の移行ビルドに引き続き取り組んでいます。重要なVue 2アプリの移行を計画している場合は、移行ビルドを待って、よりスムーズなエクスペリエンスを実現することを強くお勧めします。
最後に
Vue 3は、約100人の貢献者と30以上のRFCが関与した2年以上の開発の成果を体現した巨大なリリースです。バージョン3では、Vueは、Vueのパフォーマンスを向上させ、おそらくよりモジュール化する機能を提供します。Vue 3は、Vueを大規模 (コンポジションAPI) な場所および外部DOM環境の使用をサポートするための重要な追加機能を備えています。Vue 3は、フレームワークの成熟と、より大規模で多様なアプリケーションのセットに対する確かな技術的選択であるというその野心を反映しています。
これまでのところ、Vueは疎外的な選択をしないようにする特定の能力を示しています。たとえば、HTMLのようなテンプレートベースのSFCコンポーネントを際立たせ、同時に完全なJavaScriptレンダリング関数 (JSXを含む) を使用する可能性を提供します。Vue 3は、Vue 2のオプションAPIを維持しながら、数百のコンポーネントを備えた大規模なアプリケーション用のコンポジションAPIを追加します。
しかし、成長には一連の問題が伴います。 2019年6月のコンポジションAPIのRFCのリリースにより、開発者コミュニティ内で活発な議論が生まれました。一部の開発者は、Vueが大規模なアプリケーションのニーズにより適切に対応しようとして、平均サイズのアプリケーションを対象とする多くの開発者のベースにとって魅力的でなくなる複雑さのしきい値に達していることへの懸念を表明しました。これらの懸念は、コンポジションAPIの完全なオプトインの性質によって和らげられた可能性があり、それ自体が未使用のモジュールのコストを削減するモジュラーアーキテクチャによって強化されています。ただし、単一のパッケージでさまざまなニーズを持つ同じ品質のさまざまなセグメントで対処することから生じる緊張は、高まる可能性があります。その緊張がフレームワークをどのように前進させるかを見るのは興味深いでしょう。
著者について
Bruno Couriol氏は、電気通信の修士号、数学の理学士号、INSEADのMBAを取得しています。アクセンチュアを皮切りに、彼のキャリアのほとんどはコンサルタントとして費やされ、大企業が重要な戦略的、組織的、技術的問題に取り組むのを支援してきました。過去数年間で、彼はビジネス、テクノロジー、起業家精神の交差点に焦点を当てました。