→ Vue.js初心者レッスン1:インスタンスVue
→初心者向けVue.js、レッスン2:バインディング属性
→ Vue.js初心者レッスン3:条件付きレンダリング
→ Vue.js初心者レッスン4:リストレンダリング
→ Vue .js初心者向けレッスン5:イベント処理
→ Vue.js初心者向けレッスン6:クラスとスタイルのバインド
→ Vue.js初心者向けレッスン7:計算されたプロパティ
→ Vue.js初心者向けレッスン8:コンポーネント
→ Vue。初心者向けjsレッスン9:カスタムイベント
→初心者向けVue.jsレッスン10:フォーム
レッスンの目的
アプリケーションページにタブを配置したいと考えています。1つは訪問者が製品に関するレビューを書くことができ、もう1つは既存のレビューを表示することができます。
初期コード
これは、ファイルのコンテンツが作業のこの段階でどのように見えるかです
index.html。
<div id="app">
<div class="cart">
<p>Cart({{ cart.length }})</p>
</div>
<product :premium="premium" @add-to-cart="updateCart"></product>
</div>
では
main.js、次のコードがあります:
Vue.component('product', {
props: {
premium: {
type: Boolean,
required: true
}
},
template: `
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ title }}</h1>
<p v-if="inStock">In stock</p>
<p v-else>Out of Stock</p>
<p>Shipping: {{ shipping }}</p>
<ul>
<li v-for="(detail, index) in details" :key="index">{{ detail }}</li>
</ul>
<div
class="color-box"
v-for="(variant, index) in variants"
:key="variant.variantId"
:style="{ backgroundColor: variant.variantColor }"
@mouseover="updateProduct(index)"
></div>
<button
@click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>
Add to cart
</button>
</div>
<div>
<h2><font color="#3AC1EF">Reviews</font></h2>
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
<product-review @review-submitted="addReview"></product-review>
</div>
`,
data() {
return {
product: 'Socks',
brand: 'Vue Mastery',
selectedVariant: 0,
details: ['80% cotton', '20% polyester', 'Gender-neutral'],
variants: [
{
variantId: 2234,
variantColor: 'green',
variantImage: './assets/vmSocks-green.jpg',
variantQuantity: 10
},
{
variantId: 2235,
variantColor: 'blue',
variantImage: './assets/vmSocks-blue.jpg',
variantQuantity: 0
}
],
reviews: []
}
},
methods: {
addToCart() {
this.$emit('add-to-cart', this.variants[this.selectedVariant].variantId);
},
updateProduct(index) {
this.selectedVariant = index;
},
addReview(productReview) {
this.reviews.push(productReview)
}
},
computed: {
title() {
return this.brand + ' ' + this.product;
},
image() {
return this.variants[this.selectedVariant].variantImage;
},
inStock() {
return this.variants[this.selectedVariant].variantQuantity;
},
shipping() {
if (this.premium) {
return "Free";
} else {
return 2.99
}
}
}
})
Vue.component('product-review', {
template: `
<form class="review-form" @submit.prevent="onSubmit">
<p v-if="errors.length">
<b>Please correct the following error(s):</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
<p>
<label for="name">Name:</label>
<input id="name" v-model="name">
</p>
<p>
<label for="review">Review:</label>
<textarea id="review" v-model="review"></textarea>
</p>
<p>
<label for="rating">Rating:</label>
<select id="rating" v-model.number="rating">
<option>5</option>
<option>4</option>
<option>3</option>
<option>2</option>
<option>1</option>
</select>
</p>
<p>
<input type="submit" value="Submit">
</p>
</form>
`,
data() {
return {
name: null,
review: null,
rating: null,
errors: []
}
},
methods: {
onSubmit() {
if(this.name && this.review && this.rating) {
let productReview = {
name: this.name,
review: this.review,
rating: this.rating
}
this.$emit('review-submitted', productReview)
this.name = null
this.review = null
this.rating = null
} else {
if(!this.name) this.errors.push("Name required.")
if(!this.review) this.errors.push("Review required.")
if(!this.rating) this.errors.push("Rating required.")
}
}
}
})
var app = new Vue({
el: '#app',
data: {
premium: true,
cart: []
},
methods: {
updateCart(id) {
this.cart.push(id);
}
}
})
これは、アプリケーションが現在どのように見えるかです。

申し込みページ
仕事
現在、レビューとレビューの送信に使用されるフォームは、隣り合ったページに表示されています。これはかなり機能する構造です。しかし、時間の経過とともに、ますます多くのレビューがページに表示されると予想されます。これは、ユーザーが選択したフォームまたはレビューのリストを表示するページを操作する方が便利であることを意味します。
問題の解決策
問題を解決するために、タブのシステムをページに追加できます。そのうちの1つには、タイトルが付いており、
Reviewsレビューが表示されます。タイトル付きの2つ目は、Make a Reviewレビューを送信するためのフォームを表示します。
タブシステムを実装するコンポーネントの作成
コンポーネントを作成することから始めましょう
product-tabs。コンポーネントの視覚的表現の下部に表示されproductます。時間の経過とともに、ページにレビューとフォームのリストを表示するために現在使用されているコードが置き換えられます。
Vue.component('product-tabs', {
template: `
<div>
<span class="tab" v-for="(tab, index) in tabs" :key="index">{{ tab }}</span>
</div>
`,
data() {
return {
tabs: ['Reviews', 'Make a Review']
}
}
})
現在、これは空白のコンポーネントであり、まもなく完成する予定です。とりあえず、このコードで何が示されているかを簡単に説明しましょう。
コンポーネントデータには
tabs、タブヘッダーとして使用する文字列を含む配列があります。コンポーネントテンプレートは、v-for各配列tabs要素に<span>対応する文字列を含む要素を作成する構造を使用します。作業のこの段階でこのコンポーネントを形成するものは、次のようになります。

作業の初期段階でのproduct-tabsコンポーネント
。目標を達成するには、どのタブがアクティブであるかを知る必要があります。したがって、コンポーネントデータにプロパティを追加しましょう
selectedTab。タブタイトルのクリックに応答するイベントハンドラーを使用して、このプロパティの値を動的に設定します。
@click="selectedTab = tab"
プロパティには、タブのタイトルに対応する行が書き込まれます。
つまり、ユーザーがタブをクリックする
ReviewsとselectedTab、文字列がに書き込まれReviewsます。タブをクリックすると、Make a ReviewそのselectedTab行が含まれMake a Reviewます。
これは、完全なコンポーネントコードがどのようになるかを示しています。
Vue.component('product-tabs', {
template: `
<div>
<ul>
<span class="tab"
v-for="(tab, index) in tabs"
@click="selectedTab = tab"
>{{ tab }}</span>
</ul>
</div>
`,
data() {
return {
tabs: ['Reviews', 'Make a Review'],
selectedTab: 'Reviews' // @click
}
}
})
クラスをアクティブなタブにバインドする
タブを使用するインターフェースを使用するユーザーは、どのタブがアクティブであるかを知っている必要があります。タブ名の表示に使用される要素へのクラスバインディングを使用して、同様のメカニズムを実装でき
<span>ます。
:class="{ activeTab: selectedTab === tab }"
ここで使用されるクラスのスタイルを定義するCSSファイルは次のとおりです
activeTab。これはこのスタイルがどのように見えるかです:
.activeTab {
color: #16C0B0;
text-decoration: underline;
}
そしてここにクラススタイルがあり
tabます:
.tab {
margin-left: 20px;
cursor: pointer;
}
上記の構成を簡単な言葉で説明すると、equals
activeTabの場合、クラスに指定されたスタイルがタブに適用されることがわかります。ユーザーがクリックしたばかりのタブの名前が書き込まれるため、スタイルはアクティブなタブに特に適用されます。
最初のタブをユーザーがクリックすると、換言すれば、配置されます、同じことがに書き込まれます。その結果、スタイルは最初のタブに適用されます。
これで、ページのタブタイトルは次のようになります。selectedTabtabselectedTab.activeTab
tabReviewsselectedTab.activeTab

アクティブなタブの強調表示されたタイトル
この段階ではすべてが期待どおりに機能しているように見えるので、次に進むことができます。
コンポーネントテンプレートでの作業
どのタブがアクティブなタブであるかをユーザーに伝えることができるので、コンポーネントの作業を続けることができます。つまり、テンプレートを完成させ、各タブがアクティブ化されたときにページに正確に何が表示されるかを説明します。
ユーザーがタブをクリックした場合に何を表示するかを考えてみましょう
Reviews。もちろん、これは製品レビューです。したがって、我々は、コンポーネントにコンポーネントのテンプレートからのレビューを表示するためのコードを移動しますproductテンプレートproduct-tabs、タブのヘッダーを表示するために使用される建設以下、このコードを配置します。これは、コンポーネントテンプレートが現在どのように見えるかproduct-tabsです:
template: `
<div>
<ul>
<span class="tab"
:class="{ activeTab: selectedTab === tab }"
v-for="(tab, index) in tabs"
@click="selectedTab = tab"
>{{ tab }}</span>
</ul>
<div>
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
</div>
`
レビューのリストの上に
<h2><font color="#3AC1EF">タイトルを表示する必要がなくなったため
、タグを削除したことに注意してくださいReviews。このタイトルの代わりに、対応するタブのタイトルが表示されます。
ただし、テンプレートコードを移動するだけでは、フィードバックを提供するのに十分ではありません。
reviewsレビューの表示にデータが使用される配列は、コンポーネントデータの一部として保存されますproduct。コンポーネントのproduct-tabs小道具メカニズムを使用して、この配列をコンポーネントに渡す必要があります。作成時に使用されるオプションを使用して、オブジェクトにproduct-tabs以下を追加しましょう。
props: {
reviews: {
type: Array,
required: false
}
}
テンプレートで次の構成を使用して、コンポーネント
reviewsからコンポーネントproductに
配列を渡します。product-tabsproduct
<product-tabs :reviews="reviews"></product-tabs>
次に、ユーザーがタブのタイトルをクリックした場合にページに何を表示する必要があるかを考えてみましょう
Make a Review。もちろん、これはフィードバックを送信するためのフォームです。プロジェクトをさらに作業する準備をするために、コンポーネント接続コードproduct-reviewをコンポーネントテンプレートproductからテンプレートに転送しましょうproduct-tabs。<div>レビューのリストを表示するために使用される要素の下に次のコードを配置しましょう。
<div>
<product-review @review-submitted="addReview"></product-review>
</div>
ここでアプリケーションページを見ると、レビューのリストとフォームがタブヘッダーの下に表示されていることがわかります。

ページでの作業の中間段階
この場合、見出しをクリックすると、選択につながりますが、ページの他の要素にはまったく影響しません。さらに、フォームを使おうとすると、正常に動作しなくなったことがわかります。これらはすべて、アプリケーションに加えた変更の予想される結果です。作業を続けて、プロジェクトを稼働状態にしましょう。
ページ要素の条件付き表示
コンポーネントテンプレートの主要な要素を準備した
product-tabsので、ユーザーがクリックしたタブタイトルに基づいてさまざまなページ要素を表示できるシステムを作成します。
コンポーネントデータにはすでにプロパティがあります
selectedTab。これをディレクティブで使用して、v-show各タブに属するものを条件付きでレンダリングできます。
したがって、
<div>レビューのリストを生成するためのコードを含むタグに、次の構造を追加できます。
v-show="selectedTab === 'Reviews'"
彼女のおかげで、タブがアクティブになるとレビューのリストが表示されます
Reviews。
同様に、
<div>コンポーネント接続コードを含むタグにproduct-review以下を追加します。
v-show="selectedTab === 'Make a Review'"
これにより、タブがアクティブな場合にのみフォームが表示されます
Make a Review。
これは、コンポーネントテンプレートが現在どのように見えるか
product-tabsです:
template: `
<div>
<ul>
<span class="tab"
:class="{ activeTab: selectedTab === tab }"
v-for="(tab, index) in tabs"
@click="selectedTab = tab"
>{{ tab }}</span>
</ul>
<div v-show="selectedTab === 'Reviews'">
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
<div v-show="selectedTab === 'Make a Review'">
<product-review @review-submitted="addReview"></product-review>
</div>
</div>
`
ページを見てタブをクリックすると、作成したメカニズムが正しく機能していることを確認できます。

タブをクリックすると、一部のアイテムが非表示になり、他のアイテムが表示されます。
フォームからフィードバックを送信しても機能しません。問題を調査して修正しましょう。
フィードバックの送信に関する問題の解決
ここでブラウザ開発者ツールコンソールを見ると、警告が表示されます。

コンソールの警告
明らかに、システムはメソッドを検出できません
addReview。彼はどうなりましたか?
この質問に答えるに
addReviewは、それがコンポーネントで宣言されているメソッドであることを忘れないでproductください。コンポーネントproduct-review(およびこれはコンポーネントの子コンポーネントですproduct)がイベントを生成する場合に呼び出す必要がありますreview-submitted:
<product-review @review-submitted="addReview"></product-review>
これは、上記のコードスニペットをコンポーネントに転送する前にすべてがどのように機能したかです
product-tabs。そして今、コンポーネントproductは子コンポーネントproduct-tabsであり、product-review今では「子」、コンポーネントproductではなく、その「孫」です。
これで、コードは
product-review親コンポーネントと対話するように設計されました。しかし、今ではコンポーネントではなくなりましたproduct。その結果、フォームが正しく機能するためには、プロジェクトコードをリファクタリングする必要があることがわかりました。
プロジェクトコードのリファクタリング
孫コンポーネントとその「祖父母」との通信を確保するため、または同じレベルのコンポーネント間の通信を確立するために、グローバルイベントバスと呼ばれるメカニズムがよく使用されます。
グローバルイベントバスは、コンポーネント間で情報を転送するために使用できる通信チャネルです。実際、オプション付きのオブジェクトを渡さずに作成されるのは、Vueインスタンスだけです。イベントバスを作成しましょう:
var eventBus = new Vue()
このコードはファイルのトップレベルに移動します
main.js。
イベントバスをバスと考えると、この概念を理解しやすいかもしれません。その乗客は、一部のコンポーネントが他のコンポーネントに送信するデータです。この例では、他のコンポーネントによって生成されたイベントに関する情報を1つのコンポーネントに転送することについて話しています。つまり、「バス」はコンポーネント間
product-reviewを移動productし、フォームが送信されたという情報を伝達し、フォームデータをからproduct-reviewに配信しproductます。
これで、コンポーネント
product-reviewのメソッドにonSubmit、次のような行があります。
this.$emit('review-submitted', productReview)
eventBus代わり
に使用して、次のものに置き換えましょうthis:
eventBus.$emit('review-submitted', productReview)
その後、
review-submittedコンポーネントイベントをリッスンする必要はありませんproduct-review。したがって、コンポーネントテンプレート内のこのコンポーネントのコードを次のように変更product-tabsします。
<product-review></product-review>
これで、
productメソッドをコンポーネントから削除できますaddReview。代わりに、次の構造を使用します。
eventBus.$on('review-submitted', productReview => {
this.reviews.push(productReview)
})
以下のコンポーネントでの使用方法について説明しますが、ここでは、その中で何が起こるかを簡単に説明します。この構成は
eventBus、イベントを生成するときにreview-submitted、このイベントで渡されたデータ(つまり、- productReview)を取得して、reviewsコンポーネント配列に配置する必要があることを示していますproduct。実際、これはaddReview、私たちがもはや必要としない方法でこれまでに行われたことと非常に似ています。上記のコードスニペットは矢印関数を使用していることに注意してください。この瞬間は、より詳細な報道に値します。
矢印機能を使用する理由
ここでは、ES6で導入された矢印関数構文を使用しています。重要なのは、矢印関数のコンテキストが親コンテキストにバインドされているということです。つまり、この関数内で
thisキーワードthisを使用すると、矢印関数を含むエンティティに対応するキーワードと同等になります。
このコードは矢印関数を使用せずに書き直すことができますが、バインディングを整理する必要があります
this。
eventBus.$on('review-submitted', function (productReview) {
this.reviews.push(productReview)
}.bind(this))
プロジェクトの完了
ほぼ目標を達成しました。あとは、イベントへの応答を提供するコードの場所を見つけるだけ
review-submittedです。product関数は、コンポーネント内でそのような場所になる可能性がありますmounted。
mounted() {
eventBus.$on('review-submitted', productReview => {
this.reviews.push(productReview)
})
}
この機能は何ですか?これは、コンポーネントがDOMにマウントされた後に1回呼び出されるライフサイクルフックです。これで、コンポーネント
productがマウントされた後、イベントが発生するのを待ちますreview-submitted。このようなイベントが生成された後、このイベントで渡されたものがコンポーネントデータに追加されますproductReview。つまり、-です。
フォームを使用して製品に関するレビューを残そうとすると、このレビューが本来あるべき場所に表示されていることがわかります。

フォームは正常に機能します
イベントバスは、コンポーネントを通信するための最適なソリューションではありません
イベントバスはよく使用され、さまざまなプロジェクトで見つかる可能性がありますが、これはアプリケーションコンポーネントの接続の問題に対する最善の解決策にはほど遠いことに注意してください。
アプリケーションが大きくなるにつれて、Vuexに基づく状態管理システムが非常に便利になります。これは、アプリケーションの状態管理パターンおよびライブラリです。
ワークショップ
タブ
Shippingとをプロジェクトに追加しますDetails。これらはそれぞれ、購入品の配送コストと商品に関する情報を表示します。
結果
このチュートリアルで学んだことは次のとおりです。
- 条件付きレンダリングツールを使用して、タブのメカニズムを整理できます。
- , Vue, .
- — . . — Vuex.
このVueコースを受講した後、あなたが望むものを学び、このフレームワークについてもっと多くの新しくて興味深いことを学ぶ準備ができていることを願っています。
このコースを修了したばかりの方は、感想をお聞かせください。
→ Vue.js初心者レッスン1:インスタンスVue
→初心者向けVue.js、レッスン2:バインディング属性
→ Vue.js初心者レッスン3:条件付きレンダリング
→ Vue.js初心者レッスン4:リストレンダリング
→ Vue初心者向け.jsレッスン5:イベント処理
→初心者向けVue.js、レッスン6:クラスとスタイルのバインド
→初心者向けVue.js、レッスン7:計算プロパティ
→初心者向けのVue.js、レッスン8:コンポーネント
→初心者向けのVue.js、レッスン9:カスタムイベント
→初心者向けのVue.js、レッスン10:フォーム
