では、構築するための基礎を築きました。今こそ「本格的に」始めるときです。 前回の投稿 私は についてよく耳にしました。さらに、開発者からマネージャーに転向した友人が Vue について良いことを教えてくれたので、さらに興味が湧きました。私は Vue を見てみることにしました。これは、私が初心者の観点から学ぶ最初の「軽量」JavaScript フレームワークになります。 Vue.js 作業のレイアウト 前回の投稿では、WebJars と Thymeleaf について説明しました。サーバー側とクライアント側のセットアップは次のとおりです。 サーバー側 両方を POM に統合する方法は次のとおりです。 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!--1--> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <!--2--> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> <!--3--> <version>0.52</version> </dependency> <dependency> <groupId>org.webjars.npm</groupId> <artifactId>vue</artifactId> <!--4--> <version>3.4.34</version> </dependency> </dependencies> Spring Boot自体。通常の非リアクティブアプローチを採用することにしました。 Spring Boot Thymeleaf 統合 WebJars ロケータ。クライアント側で Vue のバージョンを指定する必要がないようにする。 ついにVue! Spring Boot 側では Kotlin Router と Bean DSL を使用しています。 fun vue(todos: List<Todo>) = router { //1 GET("/vue") { ok().render("vue", mapOf("title" to "Vue.js", "todos" to todos)) //2-3 } } オブジェクトの静的リストを渡す Todo 以下を参照してください モデルをThymeleafに渡す API の開発に慣れている方なら、 関数に馴染みがあるでしょう。この関数は、おそらく JSON 形式でペイロードを直接返します。render フローをビュー テクノロジ (この場合は Thymeleaf) に渡します。この関数は、次の 2 つのパラメータを受け入れます。 body() render() ビューの名前。デフォルトでは、パスは で、プレフィックスは です。この場合、Thymeleaf は のビューを想定しています。 /templates .html /templates/vue.html キーと値のペアのモデルマップ クライアント側 HTML 側のコードは次のとおりです。 <script th:src="@{/webjars/axios/dist/axios.js}" src="https://cdn.jsdelivr.net/npm/axios@1.7/dist/axios.min.js"></script> <!--1--> <script th:src="@{/webjars/vue/dist/vue.global.js}" src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script> <!--2--> <script th:src="@{/vue.js}" src="../static/vue.js"></script> <!--3--> <script th:inline="javascript"> /*<![CDATA[*/ window.vueData = { <!--4--> title: /*[[${ title }]]*/ 'A Title', todos: /*[[${ todos }]]*/ [{ 'id': 1, 'label': 'Take out the trash', 'completed': false }] }; /*]]>*/ </script> HTTPリクエストの作成に役立ちます Axiosは Vue自体 クライアント側のコード データを設定する 先週の記事で説明したように、Thymeleaf の利点の 1 つは、静的ファイルのレンダリングとサーバー側のレンダリングの両方が可能であることです。この魔法を機能させるには、クライアント側のパス ) とサーバー側のパス を指定します。 (つまり src (つまり th:src Vueコード それでは、Vue コードについて詳しく見ていきましょう。 いくつかの機能を実装したいと考えています: ページが読み込まれると、すべての 項目が表示されます。 Todo 完了チェックボックスをクリックすると、 属性を設定/解除する必要があります。 Todo completed ボタンをクリックすると、完了した がすべて削除されます。 クリーンアップ Todo ボタンをクリックすると、次の値を持つ リストに追加されます。 [追加] Todo Todo : サーバー側で計算されたID。他のすべてのIDの最大値に1を加えたもの id : の フィールドの値 label label ラベル : に設定 completed false Vueへの第一歩 最初のステップはフレームワークをブートストラップすることです。上記でカスタム ファイルの参照をすでに設定しています。 vue.js document.addEventListener('DOMContentLoaded', () => { //1 // The next JavaScript code snippets will be inside the block } DOMの読み込みが完了したらブロックを実行します 次のステップは、Vue にページの一部を管理させることです。HTML 側では、Vue が管理するトップレベルの部分を決定する必要があります。任意の 選択し、必要に応じて後で変更できます。 <div> <div id="app"> </div> JavaScript 側では、前の HTML の CSS セレクターを渡して を作成します。 <div> app Vue.createApp({}).mount('#app'); この時点では、ページが読み込まれると Vue が起動しますが、目に見えるようなことは何も起こりません。 次のステップは、Vue を作成することです。Vue テンプレートは、Vue によって管理される通常の HTML です。Vue は Javascript で定義できますが、HTML ページで行うことを好みます。 テンプレート <template> タイトルを表示できるルート テンプレートから始めましょう。 <template id="todos-app"> <!--1--> <h1>{{ title }}</h1> <!--2--> </template> 簡単にバインドできるようにIDを設定する プロパティを使用します。設定が残っています title JavaScript 側では、管理コードを作成する必要があります。 const TodosApp = { props: ['title'], //1 template: document.getElementById('todos-app').innerHTML, } HTMLテンプレートで使用される プロパティを宣言します。 title 最後に、アプリを作成するときにこのオブジェクトを渡す必要があります。 Vue.createApp({ components: { TodosApp }, //1 render() { //2 return Vue.h(TodosApp, { //3 title: window.vueData.title, //4 }) } }).mount('#app'); コンポーネントを構成する Vueは 関数を必要とします render() の はオブジェクトとそのプロパティから仮想ノードを作成します。 ハイパースクリプト h() サーバー側で生成された値で プロパティを初期化します title この時点で、Vue はタイトルを表示します。 基本的なやりとり この時点で、ユーザーがチェックボックスをクリックしたときのアクションを実装できます。これは、サーバー側の状態で更新する必要があります。 まず、 を表示するテーブル用に、新しいネストされた Vue テンプレートを追加しました。記事が長くなりすぎないように、詳細な説明は避けます。興味があれば、 をご覧ください。 Todo ソース コード 開始ライン テンプレートのコード (それぞれ JavaScript と HTML) は次のとおりです。 const TodoLine = { props: ['todo'], template: document.getElementById('todo-line').innerHTML } <template id="todo-line"> <tr> <td>{{ todo.id }}</td> <!--1--> <td>{{ todo.label }}</td> <!--2--> <td> <label> <input type="checkbox" :checked="todo.completed" /> </label> </td> </tr> </template> IDを表示する Todo ラベルを表示する Todo 属性が の場合はチェックボックスをオンにします completed true Vue では、 構文によるイベント処理が可能です。 @ <input type="checkbox" :checked="todo.completed" @click="check" /> ユーザーが行をクリックすると、Vue はテンプレートの 関数を呼び出します。この関数は パラメータで定義します。 check() setup() const TodoLine = { props: ['todo'], template: document.getElementById('todo-line').innerHTML, setup(props) { //1 const check = function (event) { //2 const { todo } = props axios.patch( //3 `/api/todo/${todo.id}`, //4 { checked: event.target.checked } //5 ) } return { check } //6 } } 配列を受け入れて、後でアクセスできるようにします props Vueは呼び出しをトリガーした を渡します event AxiosはHTTP呼び出しを簡素化するJavaScriptライブラリです サーバー側で API を提供する 。これはこの記事の範囲外ですが、ソース コードを自由に確認してください。 必要があります JSONペイロード 定義されたすべての関数をHTMLからアクセスできるように返します クライアント側モデル 前のセクションでは、2 つの間違いを犯しました。 ローカルモデルは管理していなかった HTTPレスポンスのcallメソッドを使用しなかった 次の機能である、完了したタスクのクリーンアップを実装することでこれを実現します。 これで、Vue 経由でイベントを処理する方法がわかりました。 <button class="btn btn-warning" @click="cleanup">Cleanup</button> オブジェクトに、同じ名前の関数を追加します。 TodosApp const TodosApp = { props: ['title', 'todos'], components: { TodoLine }, template: document.getElementById('todos-app').innerHTML, setup() { const cleanup = function() { //1 axios.delete('/api/todo:cleanup').then(response => { //1 state.value.todos = response.data //2-3 }) } return { cleanup } //1 } } 上記と同じ AxiosはHTTP呼び出しの自動JSON変換を提供します 保存する場所です state モデルを Vue のセマンティクスでは、Vue モデルは にしたいデータのラッパーです。リアクティブとは、ビューとモデル間の双方向バインディングを意味します。既存の値を メソッドに渡すことで、リアクティブにすることができます。 リアクティブ ref() Composition API では、リアクティブ状態を宣言するための推奨方法は、 関数を使用することです。 ref() 引数を受け取り、それを .value プロパティを持つ ref オブジェクト内にラップして返します。 ref() コンポーネントのテンプレート内の参照にアクセスするには、コンポーネントの 関数から参照を宣言して返します。 setup() -- リアクティブ状態の宣言 やりましょう: const state = ref({ title: window.vueData.title, //1-2 todos: window.vueData.todos, //1 }) createApp({ components: { TodosApp }, setup() { return { ...state.value } //3-4 }, render() { return h(TodosApp, { todos: state.value.todos, //5 title: state.value.title, //5 }) } }).mount('#app'); 上記の説明に従って、Thymeleafを介してHTMLページに設定されたデータを取得します。 の設定方法を変更します。双方向バインディングがないので、これは必要ではありません。クライアント側でタイトルを更新しませんが、すべての値にわたって処理の一貫性を保つことを好みます。 title Vueの期待通りにrefを返す ママ、JavaScriptのスプレッド演算子を使ってるんだよ からオブジェクトの属性を設定する state この時点で、 クライアント側モデルが完成しました。 リアクティブな HTML 側では、関連する Vue 属性を使用します。 <tbody> <tr is="vue:todo-line" v-for="todo in todos" :key="todo.id" :todo="todo"></tr> <!--1-2--> </tbody> オブジェクトのリストをループする Todo 属性は、ブラウザがHTMLを解析する方法に対処するために重要です。詳細については、 を参照してください。 is Vueのドキュメント 対応するテンプレートについては上記で説明しました。 モデルの更新 これで、クライアントから新しい 追加するという新しい機能を実装できるようになりました。 ボタンをクリックすると、 フィールドの値が読み取られ、データが API に送信され、応答でモデルが更新されます。 Todo [追加] ラベル 更新されたコードは次のとおりです。 const TodosApp = { props: ['title', 'todos'], components: { TodoLine }, template: document.getElementById('todos-app').innerHTML, setup() { const label = ref('') //1 const create = function() { //2 axios.post('/api/todo', { label: label.value }).then(response => { state.value.todos.push(response.data) //3 }).then(() => { label.value = '' //4 }) } const cleanup = function() { axios.delete('/api/todo:cleanup').then(response => { state.value.todos = response.data //5 }) } return { label, create, cleanup } } } タイトルの周りに、関数に限定されたリアクティブラッパーを作成します。 関数そのもの create() API呼び出しによって返された新しいJSONオブジェクトを リストに追加します。 Todo フィールドの値をリセットする 削除時にリスト全体を置き換えます。仕組みは同じです HTML 側では、ボタンを追加して 関数にバインドします。同様に、 フィールドを追加してモデルにバインドします。 create() Label <form> <div class="form-group row"> <label for="new-todo-label" class="col-auto col-form-label">New task</label> <div class="col-10"> <input type="text" id="new-todo-label" placeholder="Label" class="form-control" v-model="label" /> </div> <div class="col-auto"> <button type="button" class="btn btn-success" @click="create">Add</button> </div> </div> </form> Vue は、 関数を HTML ボタンにバインドします。非同期的に呼び出し、呼び出しによって返された新しい項目でリアクティブ リストを更新し 。Cleanup ボタンでも同じことを実行して、チェックされた オブジェクトを削除します。 create() Todo ます Todo コードが必要以上に複雑にならないように、意図的にエラー処理コードを実装しなかったことに注意してください。最初の経験としては十分な洞察が得られたので、ここで終わりにします。 結論 この投稿では、Vue を使用して SSR アプリを拡張する最初のステップを踏みました。これは非常に簡単でした。私が遭遇した最大の問題は、Vue が行テンプレートを置き換えることでした。ドキュメントを詳しく読んでいなかったため、 属性を見逃していました。 is しかし、HTTP 呼び出しを支援するために Axios を使用し、エラーを管理しなかったにもかかわらず、かなりの数の JavaScript 行を記述する必要がありました。 次の投稿では、同じ機能を Alpine.js で実装します。 この投稿の完全なソースコードは GitHub で見つかります: https://github.com/ajavageek/compare-frontends?embedable=true さらに詳しく: js の