こんにちは、Habr! Paul Redmondによる記事Vue.jsチュートリアル:jQueryからVue.jsへの翻訳を紹介します。
JavaScriptライブラリに関しては、jQueryほど人気のあるライブラリはありませんでした。 ブラウザの互換性が開発者にとって重要な問題であったときに、CSSセレクターを使用してDOM要素をバイパスするために作成されました。
実際、jQueryは非常に汎用性が高いため、コンポーネントJavaScriptを使用してVueでUIを作成する理由を完全に伝えると思いました。 このチュートリアルでは、最初にjQueryを使用してユーザーインターフェイスを作成し、次にVueを使用してユーザーインターフェイスを書き換えます。
プロジェクト
JavaScriptを使用して複数のフィールドを動的に追加したいフォームがある場合、非常に一般的です。 ユーザーが複数のチケットを購入できるオンライン登録フォームがあり、各チケットの名前とメールアドレスが必要だと想像してください。
これを最初にjQueryに実装することは、Vueで同じことを行う前の良いステップです。 多くの開発者はjQueryに精通しており、動的インターフェイスを作成するために使用すべき非常に異なるアプローチと優れたコントラストを提供します。
jQueryとVue on Code Pen を使用してコード例を作成しました。
jQueryを使用したバージョン
jQueryを使用してこのインターフェイスを構築する方法は多数あります。 たとえば、HTMLマークアップに1セットのフィールドを持つフォームを作成し、ユーザーがさらに追加したときにjQueryにDOMへの追加フィールドの動的追加を行わせることができます。
<script type = "text/template">
を行テンプレートとして使用し、デフォルトでDOMContentLoadedに追加することもできます。これが使用するアプローチです。
jQuery HTMLテンプレート
テンプレートの使用は、Vueでコンポーネントを作成する方法とより整合性があります。 HTMLマークアップは次のようになります。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>jQuery Checkout UI</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css"> <style type="text/css"> body { margin: 3em } button { cursor: pointer; } .unit-price { margin-right: 2rem; color: #999; } </style> </head> <body> <div class="container" id="app"> <form> <!-- A placeholder for the list of attendee inputs --> <div class="attendee-list"></div> <div class="row justify-content-center"> <div class="col-sm-6"></div> <div class="col-sm-2"> <button type="button" class="btn btn-secondary add-attendee">Add Attendee</button> </div> </div> <hr> <div class="row justify-content-center"> <div class="col-sm-6"> <!-- A placeholder for the unit price --> <span id="unit-price" class="unit-price"></span> </div> <div class="col-sm-2 text-left"> <button type="submit" id="checkout-button" class="btn btn-primary"> Pay <!-- A placeholder for the checkout total --> <span class="amount"></span></button> </div> </div> </form> </div> <script type="text/template" data-template="attendee"> <div class="attendee row justify-content-center"> <div class="col-sm-3"> <div class="form-group"> <label class="sr-only">Name</label> <input class="form-control" placeholder="Enter name" name="attendees[][name]" required> </div> </div> <div class="col-sm-3"> <div class="form-group"> <label class="sr-only">Email address</label> <input type="email" class="form-control" placeholder="Enter email" name="attendees[][email]" required> </div> </div> <div class="col-sm-2 text-left"> <button type="button" class="btn btn-light remove-attendee"> <span aria-hidden="true">×</span> Remove </button> </div> </div> </script> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> <script src="app.js"></script> </body> </html>
レイアウトにはBootstrap 4のベータ版を使用します。 $(ドキュメント).ready()のデータが入力されるjQueryプレースホルダーをいくつか特定しましたが、マークアップから何が起こっているのかを判断するのは困難です。 目的の機能の意味を理解するには、HTMLとJavaScriptを同時に見る必要があります。 数か月でこのプロジェクトに戻るには、何が起こっているのかを理解するためにかなりの精神的努力が必要です。
app.jsファイルで、1つのチケットの価格と合計価格を入力します。これは、チェックボタンでJavaScriptを使用して表示されます。 ユーザーが[参加者の追加]ボタンをクリックするたびに、テンプレートから<div class="attendee-list"></div>
コンテナーに新しい行を追加します。
参加者のリストにフォームの繰り返しフィールドを記入するには、クライアントテンプレートとして<script>
を使用します。 type = "text / template"が原因で、ブラウザはスクリプトを無視します。つまり、実行されません。
終了タグの隣で、jQueryとapp.jsの最新バージョンを使用します。このバージョンでは、動的なユーザーインターフェイスの更新の作業を開始します。
JQueryの初期化
jQueryでバージョンの作成を開始するには、フォームを初期化し、合計金額を計算し、デフォルトの行を追加して、データから価格を設定しましょう。
// app.js $(document).ready(function () { var data = { cost: 9.99 }; /** * Get the attendee count */ function getAttendeeCount() { return $('.attendee-list .row.attendee').length; } function addAttendee() { $('.attendee-list').append( $('script[data-template="attendee"]').text() ); } function syncPurchaseButton() { // Total up the count for the checkout button total $('#checkout-button span.amount').html( '$' + data.cost * getAttendeeCount() ); } // // Initialize the form // // Set up the unit cost of one ticket $('#unit-price').html('$' + data.cost + ' ea.'); // Add one attendee by default on init addAttendee(); syncPurchaseButton(); });
コードの最初の部分は、1つの価格プロパティを含むデータオブジェクトリテラルを設定します。 価格は1枚のチケットの価格です。 1つのチケットの価格を動的に設定することもできますが、ここでは単純にハードコーディングされています。
DOMクエリを使用して参加者の数を取得するなど、いくつかのヘルパー関数があります。 DOMを使用することが、jQueryを使用してこの値を決定する唯一の正確な方法です。
2番目のヘルパー関数は、マークアップのテンプレートを使用してリストに新しいメンバーを追加します。
syncPurchaseButton()関数はgetAttendeeCount()を使用して計算し、購入ボタンに最終的な金額を入力します。
テンプレートのどこでも同じ購入金額を取得したい場合は、クラスセレクターを使用してDOMのすべてのインスタンスを同期する必要がありますが、この場合は1つのみに焦点を当てています。
この時点でページをロードすると、1人のチケットの価格とチェックボタンの合計金額で、1人の訪問者によってフォームが初期化されます。
jQueryを使用してメンバーを追加する
次に、メンバーの追加と削除を検討しましょう。 jQueryには、カスタムイベントのトリガーなど、優れたイベント処理があります。 新しいメンバーを追加するために必要なコードから始めましょう。
function addAttendee() { $('.attendee-list').append( $('script[data-template="attendee"]').text() ); // Sync remove button UI syncRemoveButtons(); } function syncRemoveButtons() { // If only one attendee, hide the first remove button // otherwise, show all remove buttons if (getAttendeeCount() === 1) { $('.attendee-list .attendee .remove-attendee').first().hide(); } else { $('.attendee-list .attendee .remove-attendee').show(); } } function syncPurchaseButton() { // Total up the count for the checkout button total $('#checkout-button span.amount').html( '$' + data.cost * getAttendeeCount() ); } // Events $('.add-attendee').on('click', function (event) { event.preventDefault(); addAttendee(); $(this).trigger('attendee:add'); }).on('attendee:add', function () { syncPurchaseButton(); syncRemoveButtons(); });
syncRemoveButtons()関数は、フィールドが1つしか残っていない場合、ユーザーがフィールドを削除できないようにしますが、複数の行がある場合、ユーザーは任意の行を削除できます。
次に、addAttendee()関数でsyncRemoveButtons()を呼び出します。つまり、ページを更新すると、参加者が1人だけなので削除ボタンが非表示になります。
メンバー追加イベントハンドラーはaddAttendee()関数を呼び出し、出席者を起動します:カスタムイベントを追加します。
カスタムイベントハンドラーで、合計価格を同期してボタンが正しいようにし、次に、上記のようにsyncRemoveButtons()を呼び出して削除ボタンのステータスを更新します。
jQueryユーザーインターフェイスが大きくなると、同期ステータスが手に負えなくなる可能性があります。 状態を明示的に管理し、イベントに応じて状態が変化したときに同期する必要があります。また、各アプリケーションの状態同期の機能を理解する必要があります。
jQueryの状態管理は、さまざまな方法で処理し、DOMにリンクできるため、追加の精神的な努力が必要です。 状態がDOMに依存しており、その逆ではない場合、状態を追跡するDOM要求は複雑になります。
jQueryを使用してメンバーを削除する
この時点で、ページを更新すると、フォームに新しい行を追加できます。 最初の追加参加者を追加すると、行ごとに削除ボタンが表示され、行を削除できます。
次に、削除イベントをフックして、削除後にユーザーインターフェイスの状態が表示されることを確認します。
// Attach an event handler to the dynamic row remove button $('#app').on('click', '.attendee .remove-attendee', function (event) { event.preventDefault(); var $row = $(event.target).closest('.attendee.row'); $row.remove(); $('#app').trigger('attendee:remove'); }); $('#app').on('attendee:remove', function () { syncPurchaseButton(); syncRemoveButtons(); });
DOM ID #appにクリックイベントリスナーを追加しました。これにより、新しい行のクリックイベントに動的に応答できます。 このハンドラー内で、既定のボタンイベントを防止し、DOMツリーで最も近い.rowの祖先を見つけます。
$行の親が見つかったら、それをDOMから削除し、出席者を起動します:カスタムイベントを削除します。
出席者:イベントハンドラーの削除では、購入ボタンと削除ボタンの状態を同期します。
準備ができたjQueryバージョン
現時点では、フォームのUIのjQueryプロトタイプが機能しており、これを使用してVueバージョンと比較できます。
完全なapp.jsファイルは次のとおりです。
$(document).ready(function () { var data = { cost: 9.99 }; /** * Get the attendee count */ function getAttendeeCount() { return $('.attendee-list .row.attendee').length; } function addAttendee() { $('.attendee-list').append( $('script[data-template="attendee"]').text() ); syncRemoveButtons(); } function syncRemoveButtons() { // If only one attendee, hide the first remove button // otherwise, show all remove buttons if (getAttendeeCount() === 1) { $('.attendee-list .attendee .remove-attendee').first().hide(); } else { $('.attendee-list .attendee .remove-attendee').show(); } } function syncPurchaseButton() { // Total up the count for the checkout button total $('#checkout-button span.amount').html( '$' + data.cost * getAttendeeCount() ); } // Events $('.add-attendee').on('click', function (event) { event.preventDefault(); addAttendee(); $(this).trigger('attendee:add'); }).on('attendee:add', function () { syncPurchaseButton(); syncRemoveButtons(); }); // Attach an event handler to the dynamic row remove button $('#app').on('click', '.attendee .remove-attendee', function (event) { event.preventDefault(); var $row = $(event.target).closest('.attendee.row'); $row.remove(); $('#app').trigger('attendee:remove'); }); $('#app').on('attendee:remove', function () { syncPurchaseButton(); syncRemoveButtons(); }); // // Initialize the form // // Set up the unit cost of one ticket $('#unit-price').html('$' + data.cost + ' ea.'); // Add one attendee by default on init addAttendee(); syncPurchaseButton(); });
この例の目的は、おそらく作成したユーザーインターフェイスの種類を示し、Vue.jsと比較してどのように見えるかを示すことです。 ここで重要な結論は、DOMに直接バインドされた状態です。DOMにクエリを実行して、状態に関する結論を出す必要があります。
JQueryでもユーザーインターフェイスを作成できますが、Vueを使用して同じ機能を作成する方法を見てみましょう。
Vueの概要
ほとんどの人はおそらく現時点でVueを聞いたことがあるでしょうが、Vueに慣れていない人にとっては、このガイドは最適な場所です。
他のフレームワークとの比較は、すでに慣れ親しんでいる他のフレームワークとは対照的に、Vueを認識するのにも役立ちます。
ChromeとFirefoxで利用可能なVue devtools拡張機能をインストールすることをお勧めします。 開発者ツールは、Vueでアプリケーションを学習および開発する際に優れたデバッグ情報を提供します。
Vueを使用したバージョン
Vueのバージョンは、通常のJavaScriptを使用して記述され、ES6ツールを心配せずにコンポーネントの例に集中する必要がなくなります。
リアクティブなアプローチでデータを表示することで、Vueがユーザーインターフェイスの表示からデータを分離するのにどのように役立つかがわかります。 また、jQueryでの処理をReactまたはVueと比較したときに、DOM内を移動して不自然に見える値を計算する必要もありません。
開始
テンプレートとJavaScriptを作成する前に、フォームを作成するためのアプローチについて説明しましょう。 フォームに関連付けられたデータについて考えて、参加者のコレクション(配列)と1つの要素の価格を提示します。
リテラルオブジェクトは次のようになります。
var data = { attendees: [ { name: 'Example', email: 'user@example.com' } ], cost: 9.99, };
別の参加者を追加してデータを更新すると、Vueはリッスンし、このデータ変更に対応する準備が整います。
data.attendees.push({ name: 'Example 2', email: 'user2@example.com' });
それを念頭に置いて、ユーザーインターフェイス用のいくつかの大まかなHTMLマークアップとJavaScriptスケルトンを構築しましょう。
Vue HTMLテンプレート
jQueryバージョンで既に説明したすべての機能を使用できるように、JavaScriptとHTMLを徐々に構築していきます。
このガイドのVue部分の最初のHTMLマークアップは次のとおりです。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Vue Checkout UI</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css"> <style type="text/css"> body { margin: 3em } button { cursor: pointer; } .unit-price { margin-right: 2rem; color: #999; } </style> </head> <body> <div class="container" id="app"> <form> <div class="row justify-content-center" v-for="(attendee, index) in attendees" :key="index" > <div class="col-sm-3"> <div class="form-group"> <label class="sr-only">Name</label> <input class="form-control" aria-describedby="emailHelp" placeholder="Enter name" v-model="attendee.name" name="attendees[][name]" required > </div> </div> <div class="col-sm-3"> <div class="form-group"> <label class="sr-only">Email address</label> <input type="email" class="form-control" placeholder="Enter email" v-model="attendee.email" name="attendees[][email]" required > </div> </div> <div class="col-sm-2 text-left"> <button type="button" class="btn btn-light"> <span aria-hidden="true">×</span> Remove</button> </div> </div> <div class="row justify-content-center"> <div class="col-sm-6"></div> <div class="col-sm-2"> <button type="button" class="btn btn-secondary">Add Attendee</button> </div> </div> <hr> <div class="row justify-content-center"> <div class="col-sm-6"> <span class="unit-price">${{ cost }} ea.</span> </div> <div class="col-sm-2 text-left"> <button type="submit" class="btn btn-primary">Pay</button> </div> </div> <form> </div> <script src="https://unpkg.com/vue@2.4.4/dist/vue.js"></script> <script src="app.js"></script> </body> </html>
マークアップはjQueryバージョンに非常に似ていますが、1つの要素の価格の変数に気付いたかもしれません。
<span class="unit-price">${{ cost }} ea.</span>
Vueは宣言型レンダリングを使用してDOMにデータをレンダリングします。 {{cost}}-「口ひげ」構文とドル記号$を使用したデータバインディング。
コストプロパティを持つデータオブジェクトを覚えていますか?
var data = { cost: 9.99 };
関連するデータが変更されると、mustacheタグはdata.costに置き換えられます。
次に、行v-for = "(出席者、インデックス)in出席者"に注意してください。これは、出席者データ配列を通過し、各参加者のフォーム入力フィールドを表示するループです。
v-for属性は、「この式の値を更新するときに、DOMに変更を反応的に適用する」 ディレクティブです。 この例では、data.attendees配列が更新されると、このディレクティブの結果としてDOMが更新されます。
テンプレートの表示を開始する必要があります。データ(状態)を変更すると、ユーザーインターフェイスがこれらの変更に応答します。 その結果、コードはより宣言的で記述しやすくなります。
Vueの初期化
HTMLマークアップの下部には、app.jsスクリプトをVueコードに接続するタグがあります。
ページ上のVueインスタンスを初期化するには、VueをDOMノードに接続する必要があります。 コンテナ</ div>を提供しました。つまり、このDOM要素内のマークアップはすべてVueに関連付けられ、データの変更に応答します。
(function () { var app = new Vue({ el: '#app', data: { attendees: [{ name: '', email: '' }], cost: 9.99, }, }); })();
DOM要素#appに関連付けられた新しいVueインスタンスを作成し、メインデータオブジェクトを定義します。 データオブジェクトには、1つの要素と参加者の配列のコストが含まれます。 空白の参加者を1人追加したため、フォームにはデフォルトで1セットの入力フィールドが表示されます。
すべてのメンバーを削除して空の配列にする場合、名前やメールは表示されません。
これらはすべて、すぐに呼び出される関数式(IIFE)でラップされ、グローバルスコープからインスタンスが除外されます。
合計価格の計算
jQueryバージョンでは、メンバーの削除または追加イベントを使用して数量をDOMと同期することにより、合計価格を計算しました。 Vueでは、ご想像のとおり、データを使用し、ビューはこれらの変更に自動的に応答します。
次のようなことができますが、DOMリクエストよりもはるかに優れています。
<button type="submit" class="btn btn-primary" > Pay ${{ cost * attendees.length }} </button>
ただし、テンプレートのロジックが多すぎると、テンプレートの表現力が低下し、保守が困難になります。 代わりに、 計算されたプロパティを使用できます 。
(function () { var app = new Vue({ el: '#app', data: { attendees: [{ name: '', email: '' }], cost: 9.99, }, computed: { quantity: function () { return this.attendees.length; }, checkoutTotal: function () { return this.cost * this.quantity; } } }); })();
2つの計算されたプロパティを定義しました。 最初のプロパティはチケットの数で、参加者の配列の長さによって計算されます。
2番目の計算プロパティはcheckoutTotalで、最初の計算プロパティを使用して要素の数にコストを掛けます。
これで、計算されたプロパティを使用してチェックボタンを更新できます。 結果として計算されたプロパティの名前がどのように記述されるかに注意してください。
<button type="submit" class="btn btn-primary" > Pay ${{ checkoutTotal }} </button>
ブラウザを更新すると、ボタンで自動的に計算された合計金額が表示されます。
メンバーを追加すると、計算されたプロパティが自動的に更新され、DOMに表示されます。
Vueでメンバーを追加する
Vueイベントを使用してメンバーを追加する方法を確認する準備ができました。
jQueryでは、DOMイベントハンドラーを使用しました。
$('.add-attendee').on('click', function () {});
Vueでは、テンプレートでイベントをフックします。 私の意見では、これによりHTMLが読みやすくなります。なぜなら、この要素に関連付けられているイベントを見つけるための表現力のある方法があるからです。
v-onを使用できます:click = "addAttendee":
<!-- Using v-on: --> <button type="button" class="btn btn-secondary" v-on:click="attendees.push({ name: '', email: ''})" > Add Attendee </button>
または、 click =” addAttendee”の短縮版:
<!-- Using @click --> <button type="button" class="btn btn-secondary" @click="attendees.push({ name: '', email: ''})" > Add Attendee </button>
どのスタイルを使用しても構いませんが、1つの方法に固執することをお勧めします。 短縮スタイルが好きです。
ボタンをクリックすると、新しいオブジェクトをテンプレートの出席者配列に配置します。 属性でJavaScriptを実行できることを理解できるように、このスタイルを紹介したかったのです。
ほとんどの場合、イベントハンドラーを使用することをお勧めします。原則として、イベントにはより複雑なロジックが関連付けられているためです。
<button type="button" class="btn btn-secondary" @click="addAttendee" > Add Attendee </button>
Vueは、メインVueオブジェクト(およびコンポーネント)のメソッドのプロパティを受け入れます。これにより、イベントハンドラーメソッドを定義できます。
(function () { var app = new Vue({ el: '#app', data: { attendees: [{ name: '', email: '' }], cost: 9.99, }, computed: { quantity: function () { return this.attendees.length; }, checkoutTotal: function () { return this.cost * this.quantity; } }, methods: { addAttendee: function (event) { event.preventDefault(); this.attendees.push({ name: '', email: '', }); } } }); })();
デフォルトのアクションを防止し、新しいオブジェクトを出席者配列に配置します。 参加者を追加すると、新しく追加された入力フィールドが表示され、checkoutTotalは行数に対応します。
ハンドラーはイベントオブジェクトを受け取ることに注意してください。これを使用して、デフォルトのアクションを防ぐことができます。 デフォルトのイベントのアクションまたは伝播の停止(event.stopPropagation())を防ぐために、Vueはピリオド(。)とともに使用されるイベント修飾子を提供します。属性の一部として:
<button type="button" class="btn btn-secondary" @click.prevent="addAttendee" > Add Attendee </button>
メソッドはデータ指向であり、Vueはイベント修飾子を使用してDOMイベントを自動的に処理します。
Vueからメンバーを削除する
参加者の削除は参加者の追加に似ていますが、オブジェクトを配列に追加する代わりに、別のイベントハンドラーの配列のインデックスに基づいて削除する必要があります。
<button type="button" class="btn btn-light" @click.prevent="removeAttendee(index)" > <span aria-hidden="true">×</span> Remove </button>
配列インデックスを使用して、削除する目的の訪問者を参照します。 v-forループで思い出すと、インデックスを定義しました。
<div class="row justify-content-center" v-for="(attendee, index) in attendees" :key="index" > <!-- Attendee inputs --> </div>
Vueインスタンス内で、インデックスに基づいて配列から1つの要素を削除するスプライスを使用するremoveAttendeeメソッドを定義します。
methods: { removeAttendee: function (index) { this.attendees.splice(index, 1); }, // ... }
removeAttendeeハンドラーを使用すると、メンバーを追加および削除できます!
また、数人の参加者を追加する場合にのみ「削除」ボタンを表示して、ビジネス要件を満たしたいと考えています。 ユーザーがすべての参加者を削除することは許可しません。
組み込みのv-show条件ディレクティブを使用してこれを行うことができます。
<button type="button" class="btn btn-light" @click.prevent="removeAttendee(index)" v-show="quantity > 1" > <span aria-hidden="true">×</span> Remove </button>
計算されたプロパティ数量を使用して、数量が1より大きい場合に削除ボタンを表示しました。
v-if条件でボタンを非表示にすることもできます。 これらの仕組みのニュアンスを理解するために、ドキュメントを読むことをお勧めします。
この場合、v-showを使用して、CSSを使用してボタンを表示および非表示にします。 v-ifを使用してコードを変更し、DOMを見ると、VueがDOMから要素を削除していることがわかります。
Ready Vueバージョン
Vueの最終バージョンは次のとおりです。
(function () { var app = new Vue({ el: '#app', data: { attendees: [{ name: '', email: '' }], cost: 9.99, }, computed: { quantity: function () { return this.attendees.length; }, checkoutTotal: function () { return this.cost * this.quantity; } }, methods: { removeAttendee: function (index) { this.attendees.splice(index, 1); }, addAttendee: function (event) { event.preventDefault(); this.attendees.push({ name: '', email: '', }); } } }); })();
! , DOM .
Vue , jQuery . , , jQuery . , , UI HTML.
次は?
:
JQuery, React, Vue, . Vue , ( ).
, , .
— .