Vue 3の新機能

公開: 2022-03-10
簡単なまとめ↬Vue3には、フレームワークを使用した開発をはるかに簡単で保守しやすくすることを目的とした、多くの興味深い新機能と既存の機能への変更が含まれています。 この記事では、これらの新機能のいくつかと、それらの使用を開始する方法について説明します。 また、既存の機能に加えられた変更のいくつかを見ていきます。

Vue 3のリリースでは、開発者はVue 2からアップグレードする必要があります。これは、読みやすく保守しやすいコンポーネントの構築に非常に役立ついくつかの新機能と、Vueでアプリケーションを構造化するための改善された方法が付属しているためです。 この記事では、これらの機能のいくつかを見ていきます。

このチュートリアルの最後に、読者は次のことを行います。

  1. provide / injectとその使用方法について知ってください。
  2. テレポートとその使用方法の基本を理解している。
  3. フラグメントとその使用方法について知ってください。
  4. GlobalVueAPIに加えられた変更について知ってください。
  5. イベントAPIに加えられた変更について知ってください。

この記事は、Vue2.xを正しく理解している人を対象としています。 この例で使用されているすべてのコードはGitHubにあります。

provide / inject

Vue 2.xには、親コンポーネントから子コンポーネントに直接データ(文字列、配列、オブジェクトなど)を簡単に渡すことができるpropsがありました。 しかし、開発中に、親コンポーネントから深くネストされたコンポーネントにデータを渡す必要がある場合がよくありました。これは、 propsでは行うのがより困難でした。 その結果、Vuex Store、Event Hubが使用され、場合によっては、深くネストされたコンポーネントを介してデータが渡されました。 簡単なアプリを見てみましょう。

Vue 2.2.0には、一般的なアプリケーションコードでの使用が推奨されていないprovide / injectも付属していることに注意してください。

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" :color="color" /> <select name="color" v-model="color"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { color: "", colors: ["red", "blue", "green"], }; }, }; </script>
 # childComponent.vue <template> <div class="hello"> <h1>{{ msg }}</h1> <color-selector :color="color"></color-selector> </div> </template> <script> import colorSelector from "@/components/colorComponent.vue"; export default { name: "HelloWorld", components: { colorSelector, }, props: { msg: String, color: String, }, }; </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h3 { margin: 40px 0 0; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
 # colorComponent.vue <template> <p :class="[color]">This is an example of deeply nested props!</p> </template> <script> export default { props: { color: String, }, }; </script> <style> .blue { color: blue; } .red { color: red; } .green { color: green; } </style>

ここでは、色のリストを含むドロップダウンのあるランディングページがあり、選択したcolorを小道具としてchildComponent.vueに渡します。 この子コンポーネントには、テンプレートセクションに表示するテキストを受け入れるmsgもあります。 最後に、このコンポーネントには、このコンポーネントのテキストのクラスを決定する際に使用される親コンポーネントからcolorプロップを受け入れる子コンポーネント( colorComponent.vue )があります。 これは、すべてのコンポーネントを介してデータを渡す例です。

しかし、Vue 3では、新しいプロバイドとインジェクトのペアを使用して、よりクリーンで短い方法でこれを行うことができます。 名前が示すように、関数またはオブジェクトとしてprovideを使用して、そのようなコンポーネントがどれほど深くネストされているかに関係なく、親コンポーネントからそのネストされたコンポーネントのいずれかにデータを利用できるようにします。 このようにprovideにハードコードされた値を渡すときに、オブジェクトフォームを利用します。

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" :color="color" /> <select name="color" v-model="color"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { colors: ["red", "blue", "green"], }; }, provide: { color: 'blue' } }; </script>

ただし、コンポーネントインスタンスプロパティを渡してprovide必要がある場合は、関数モードを使用するため、これが可能です。

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" /> <select name="color" v-model="selectedColor"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { selectedColor: "blue", colors: ["red", "blue", "green"], }; }, provide() { return { color: this.selectedColor, }; }, }; </script>

childComponent.vuecolorComponent.vueの両方にcolorプロップは必要ないので、それを取り除きます。 provideを使用することの良い点は、親コンポーネントが、提供しているプロパティを必要とするコンポーネントを知る必要がないことです。

この場合、これを必要とするコンポーネントでこれを利用するには、 colorComponent.vueを実行します。

 # colorComponent.vue <template> <p :class="[color]">This is an example of deeply nested props!</p> </template> <script> export default { inject: ["color"], }; </script> <style> .blue { color: blue; } .red { color: red; } .green { color: green; } </style>

ここでは、コンポーネントが必要とする必要な変数の配列を取り込むinjectを使用します。 この場合、必要なのはcolorプロパティのみなので、それを渡すだけです。 その後、小道具を使用するときと同じようにcolorを使用できます。

ドロップダウンを使用して新しい色を選択しようとすると、 colorComponent.vueで色が更新されないことに気付く場合があります。これは、デフォルトで、 provideのプロパティがリアクティブではないためです。 それを修正するために、 computedれた方法を利用します。

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" /> <select name="color" v-model="selectedColor"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; import { computed } from "vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { selectedColor: "", todos: ["Feed a cat", "Buy tickets"], colors: ["red", "blue", "green"], }; }, provide() { return { color: computed(() => this.selectedColor), }; }, }; </script>

ここでは、 computedれたものをインポートして、 selectedColorを渡します。これにより、ユーザーが別の色を選択したときに反応して更新できるようになります。 変数を計算されたメソッドに渡すと、 valueを持つオブジェクトが返されます。 このプロパティは変数のを保持するため、この例では、 colorComponent.vueを次のように更新する必要があります。

 # colorComponent.vue <template> <p :class="[color.value]">This is an example of deeply nested props!</p> </template> <script> export default { inject: ["color"], }; </script> <style> .blue { color: blue; } .red { color: red; } .green { color: green; } </style>

ここでは、 computedれた方法を使用してcolorを反応性にした後の変化を表すために、 colorcolor.valueに変更します。 この時点で、このコンポーネントのテキストのclassは、親コンポーネントでselectedColorが変更されるたびに常に変更されます。

ジャンプした後もっと! 以下を読み続けてください↓

テレポート

アプリが使用するロジックのために、コンポーネントを作成してアプリケーションの一部に配置する場合がありますが、アプリケーションの別の部分に表示することを目的としています。 この一般的な例は、画面全体を表示およびカバーすることを目的としたモーダルまたはポップアップです。 このような要素でCSSのpositionプロパティを使用してこの回避策を作成できますが、Vue 3では、Teleportを使用して回避することもできます。

テレポートを使用すると、コンポーネントをドキュメント内の元の位置から取り出し、デフォルトの#appコンテナーからVueアプリをラップして、使用しているページ上の既存の要素に移動できます。 良い例は、テレポートを使用してヘッダーコンポーネントを#app内からheaderに移動することです。VueDOMの外部に存在する要素にのみテレポートできることに注意することが重要です。

無効な要素にテレポートしたときのコンソールのエラーメッセージ
コンソールのテレポートエラーメッセージ:ターミナルのテレポートターゲットエラーメッセージが無効です。 (大プレビュー)

テレポートコンポーネントは、このコンポーネントの動作を決定する2つの小道具を受け入れます。

  1. to
    このpropは、クラス名、ID、要素、またはdata- *属性のいずれかを受け入れます。 テレポート要素を動的に変更toのではなく、 :to propを渡すことで、この値を動的にすることもできます。
  2. :disabled
    この小道具はBooleanを受け入れ、要素またはコンポーネントのテレポート機能を切り替えるために使用できます。 これは、要素の位置を動的に変更する場合に役立ちます。

テレポートを使用する理想的な例は次のようになります。

 # index.html** <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width,initial-scale=1.0" /> <link rel="icon" href="<%= BASE_URL %>favicon.ico" /> <title> <%= htmlWebpackPlugin.options.title %> </title> </head> <!-- add container to teleport to --> <header class="header"></header> <body> <noscript> <strong >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong > </noscript> <div></div> <!-- built files will be auto injected --> </body> </html>

Vueアプリのデフォルトのindex.htmlファイルでは、ヘッダーコンポーネントをアプリのそのポイントにテレポートするため、 header要素を追加します。 また、この要素にクラスを追加して、スタイリングとTeleportコンポーネントでの参照を容易にしました。

 # Header.vue** <template> <teleport to="header"> <h1 class="logo">Vue 3 </h1> <nav> <router-link to="/">Home</router-link> </nav> </teleport> </template> <script> export default { name: "app-header", }; </script> <style> .header { display: flex; align-items: center; justify-content: center; } .logo { margin-right: 20px; } </style>

ここでは、ヘッダーコンポーネントを作成し、アプリのホームページへのリンクを含むロゴを追加します。 また、Teleportコンポーネントを追加し、このコンポーネントをこの要素内にレンダリングするto 、topropにheaderの値を指定します。 最後に、このコンポーネントをアプリにインポートします。

 # App.vue <template> <router-view /> <app-header></app-header> </template> <script> import appHeader from "@/components/Header.vue"; export default { components: { appHeader, }, }; </script>

このファイルでは、ヘッダーコンポーネントをインポートしてテンプレートに配置し、アプリで表示できるようにします。

ここで、アプリの要素を調べると、ヘッダーコンポーネントがheader要素内にあることがわかります。

DevToolsのヘッダーコンポーネント
DevToolsのヘッダーコンポーネント(大プレビュー)

フラグメント

Vue 2.xでは、ファイルのtemplateに複数のルート要素を含めることは不可能でした。回避策として、開発者はすべての要素を親要素にラップし始めました。 これは深刻な問題のようには見えませんが、開発者がそのような要素をコンテナでラップせずにコンポーネントをレンダリングしたいが、それを行わなければならない場合があります。

Vue 3では、フラグメントと呼ばれる新機能が導入されました。この機能により、開発者はルートテンプレートファイルに複数の要素を含めることができます。 したがって、Vue 2.xでは、これは入力フィールドコンテナーコンポーネントがどのように見えるかを示しています。

 # inputComponent.vue <template> <div> <label :for="label">label</label> <input :type="type" : :name="label" /> </div> </template> <script> export default { name: "inputField", props: { label: { type: String, required: true, }, type: { type: String, required: true, }, }, }; </script> <style></style>

ここでは、 labeltypeの2つの小道具を受け入れる単純なフォーム要素コンポーネントがあり、このコンポーネントのテンプレートセクションはdivでラップされています。 これは必ずしも問題ではありませんが、ラベルと入力フィールドをform要素内に直接配置する場合。 Vue 3を使用すると、開発者はこのコンポーネントを次のように簡単に書き直すことができます。

 # inputComponent.vue <template class="testingss"> <label :for="label">{{ label }}</label> <input :type="type" : :name="label" /> </template>

ルートノードが1つしかない場合、属性は常にルートノードに関連付けられ、非プロップ属性とも呼ばれます。 これらは、 propsまたはemitsで定義された対応するプロパティを持たないコンポーネントに渡されるイベントまたは属性です。 このような属性の例は、 classidです。 ただし、マルチルートノードコンポーネントのどの要素に帰属するかを明示的に定義する必要があります。

上からinputComponent.vueを使用することの意味は次のとおりです。

  1. 親コンポーネントのこのコンポーネントにclassを追加するときは、このclassがどのコンポーネントに帰属するかを指定する必要があります。そうしないと、属性は効果がありません。
 <template> <div class="home"> <div> <input-component class="awesome__class" label="name" type="text" ></input-component> </div> </div> </template> <style> .awesome__class { border: 1px solid red; } </style>

属性の属性を定義せずにこのようなことを行うと、コンソールにこの警告が表示されます。

属性が配布されていない場合のターミナルのエラーメッセージ
属性が配布されていない場合のターミナルのエラーメッセージ(大プレビュー)

また、 borderはコンポーネントに影響を与えません。

属性分散のないコンポーネント
属性分散のないコンポーネント(大プレビュー)
  1. これを修正するには、そのような属性を配布する要素にv-bind="$attrs"を追加します。
 <template> <label :for="label" v-bind="$attrs">{{ label }}</label> <input :type="type" : :name="label" /> </template>

ここでは、属性をlabel要素に分散させたい、 awesome__classをそれに適用したいということをVueに伝えています。 ここで、ブラウザで要素を調べると、クラスがlabelに追加されていることがわかります。したがって、ラベルの周囲に境界線があります。

属性分散のあるコンポーネント
属性分布のあるコンポーネント(大プレビュー)

グローバルAPI

Vueアプリケーションのmain.jsファイルにVue.componentまたはVue.useが表示されることは珍しくありませんでした。 これらのタイプのメソッドはグローバルAPIとして知られており、Vue2.xにはかなりの数のメソッドがあります。 この方法の課題の1つは、他のアプリに影響を与えることなく、特定の機能をアプリの1つのインスタンスに分離できないことです(アプリに複数のインスタンスがある場合)。これらはすべてVueにマウントされているためです。 これが私の言いたいことです。

 Vue.directive('focus', { inserted: el => el.focus() }) Vue.mixin({ /* ... */ }) const app1 = new Vue({ el: '#app-1' }) const app2 = new Vue({ el: '#app-2' })

上記のコードでは、Vueディレクティブがapp1に関連付けられ、Mixinがapp2に関連付けられていることを示すことはできませんが、代わりに、2つのアプリで両方を使用できます。

Vue 3には、 createAppの導入によりこのタイプの問題を修正するために、新しいグローバルAPIが付属しています。 このメソッドは、Vueアプリの新しいインスタンスを返します。 アプリインスタンスは、現在のグローバルAPIのサブセットを公開します。 これにより、 Vue 2.xからVueを変更するすべてのAPI(コンポーネント、ミックスイン、ディレクティブ、使用など)が個々のアプリインスタンスに移動され、Vueアプリの各インスタンスが固有の機能を持つことができるようになります。他の既存のアプリに影響を与えることなくそれらを。

これで、上記のコードは次のように書き直すことができます。

 const app1 = createApp({}) const app2 = createApp({}) app1.directive('focus', { inserted: el => el.focus() }) app2.mixin({ /* ... */ })

ただし、すべてのアプリ間で共有したい機能を作成することは可能であり、これはファクトリ関数を使用して行うことができます。

イベントAPI

Vuexストアを使用する以外に親子関係を持たないコンポーネント間でデータを渡すために開発者が採用した最も一般的な方法の1つは、イベントバスの使用です。 この方法が一般的である理由の1つは、使い始めるのがいかに簡単かということです。

 # eventBus.js const eventBus = new Vue() export default eventBus;

この後、次は、このファイルをmain.jsにインポートして、アプリでグローバルに利用できるようにするか、必要なファイルにインポートします。

 # main.js import eventBus from 'eventBus' Vue.prototype.$eventBus = eventBus

これで、イベントを発行し、このように発行されたイベントをリッスンできます。

 this.$eventBus.$on('say-hello', alertMe) this.$eventBus.$emit('pass-message', 'Event Bus says Hi')

このようなコードで満たされたVueコードベースはたくさんあります。 ただし、Vue 3では、 $on$off 、および$onceがすべて削除されているため実行できませんが、子コンポーネントが親コンポーネントにイベントを発行する必要があるため、 $emitは引き続き使用できます。 これに代わる方法は、 provide / injectまたは推奨されるサードパーティライブラリのいずれかを使用することです。

結論

この記事では、 provide / injectペアを使用して、親コンポーネントから深くネストされた子コンポーネントにデータを渡す方法について説明しました。 また、アプリ内のあるポイントから別のポイントにコンポーネントを再配置して転送する方法についても見てきました。 私たちが見たもう1つのことは、マルチルートノードコンポーネントと、属性が正しく機能するように属性を確実に配布する方法です。 最後に、EventsAPIとGlobalAPIの変更についても説明しました。

その他のリソース

  • 「ES6 +でのJavaScriptファクトリ関数」、Eric Elliott、Medium
  • 「イベントバスを使用してVueコンポーネント間で小道具を共有する」、Kingsley Silas、CSS-Tricks
  • 同じターゲットで複数のテレポートを使用するVue.jsDocs
  • 非プロップ属性、Vue.jsドキュメント
  • 反応性の操作、Vue.jsドキュメント
  • teleport 、Vue.jsドキュメント
  • フラグメント、Vue.jsドキュメント
  • 2.x構文、Vue.jsドキュメント