@@ -15,15 +15,15 @@ Vue.jsは日本で人気のあるウェブフロントエンドのフレーム
1515
1616次のオプションで必要なものをスペースキーで選択して、エンターで次に進みます。選んだ項目によって追加の質問が行われます。Routerやステート管理などのアプリケーション側の機能に関する項目以外にも、LinterやユニットテストフレームワークやE2Eテストの補助ツールなど、さまざまなものを選択できます。
1717
18- .. figure :: images/vue-cli-2 .png
18+ .. figure :: images/vue-cli-1 .png
1919
20- 必要な機能を選択する
20+ TypeScriptを選択する場合はManually select featuresを選択
2121
2222途中で、クラスベースかそうではないか、という質問が出てきます。以前ではクラスベースのAPIの方がTypeScriptとの相性がよかったのですが、Vue.js3からの新しいAPIで、クラスベースでない時も型チェックなどに優しいComposition APIが追加されました。そこはチームで好きな方を選択すれば良いですし、あとから別のスタイルにすることもできます。
2323
24- .. figure :: images/vue-cli-3 .png
24+ .. figure :: images/vue-cli-2 .png
2525
26- クラススタイルのコンポーネントを利用するか?
26+ 必要な機能を選択する
2727
2828現在のVue.jsのプロジェクトのほとんどは、\ ``.vue ``\ ファイルに記述するシングルファイルコンポーネント(SFC)を使っていると思いますが、TypeScriptを使う場合、スクリプトタグの\ ``lang ``\ 属性を\ ``ts ``\ になっています。
2929
@@ -139,3 +139,288 @@ Vue本体で提供されている\ ``defineComponent()``\ 関数を使いコン
139139 Nuxt.jsの場合は、通常の設定の後に、いくつか追加のパッケージのインストールや設定が必要です。日本語によるガイドもあります。
140140
141141 * https://typescript.nuxtjs .org/ja/guide/setup.html
142+
143+ ただし、現時点ではVue.js 3対応はまだ計画中でリリースはまだ行われていません。
144+
145+ Vue.jsを使ったjQueryのリプレース
146+ --------------------------------------------------------
147+
148+ jQueryは歴史があるライブラリで、使い勝手の良さから、非フロントエンド開発者にも広く普及しました。一方で、開発が大規模化する場合に整合性をとるのが難しくなってくることが多く、フロントエンドの比重が高まるにつれて、ReactやVue.jsを使う人が増えています。
149+
150+ jQueryからVue.jsへはパラダイムがかなり違うので、多少コーディングが必要となります。jQueryは、セレクタでマッチしたHTMLのタグを直接変更していきます。一方、最近のウェブフロントエンドのフレームワークはTypeScript内部に状態を持ち、それを画面に反映させる、という形をとります。反映するときはテンプレートエンジンのような記法を用いて表現します。VueやReactは仮想DOMという仕組みを使っており、ビュー関数を頻繁に実行し、その結果を画面に反映します。
151+
152+ jQueryの方が、極めて簡単なことをする場合は短いコードで済むことがあります。一方、変更が多くなると更新が複雑になります。
153+
154+ * 同じ値を何度も表示する場合、VueやReactの場合、大元の変数を変更するとすべての箇所が変わります。jQueryでは利用箇所をすべて自分で見つけて更新しなければなりません。
155+ * テーブル表示など、表示先の階層が深くて場所の特定も大変な場合にはロジックが複雑になります。
156+ * 確認ダイアログを出してOK/Cancel時に別のダイアログを出してという場合には、次のダイアログを表示にする、前のダイアログを非表示にする、といったように、すべての変更を1つずつ適用していかなければなりません。状態遷移が複雑になってくると、一箇所の修正漏れで画面の遷移がおかしくなります。VueやReactであれば、現在の遷移はどこか、という情報を1つもち、それをみるようにすると、扱う状態が少ない分、ミスが減ります。
157+
158+ 次のようなシンプルなjQueryのコードをVue.jsにしてみましょう。
159+
160+ .. code-block :: html
161+
162+ <div > jQuery test page</div >
163+ <button class="pushbutton"> button</button >
164+ <div class="panel" style="display: none; background-color : lightblue;">
165+ hidden
166+ </div >
167+
168+ <style scoped>
169+ .clickedButton {
170+ background-color : " yellow" ;
171+ }
172+ </style >
173+
174+ <script >
175+ $ (function () {
176+ $ (" .pushbutton" ).click (function () {
177+ $ (" .pushbutton" ).addClass (" clickedButton" );
178+ $ (" .panel" ).fadeIn ();
179+ });
180+ });
181+ </script >
182+
183+ ここで使っているjQueryの機能は3つです。
184+
185+ * ``click() ``\ でボタンのクリックのイベントハンドラの設定
186+ * クリック時に\ ``addClass() ``\ でCSSのスタイルの変更
187+ * クリック時に、\ ``fadeIn() ``\ を使って隠された要素の表示
188+
189+ まずはそのまま.vueファイル化
190+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191+
192+ まずはプロジェクトを作り、既存のHTMLファイルを取り込みます。この説明では次の想定で進めます。
193+
194+ * プロジェクトはvue-cliで作成
195+ * 対象バージョンはVue.js 3で、クラス形式のコンポーネントではない関数ベースのコンポーネントを利用
196+ * ルーターを利用
197+
198+ jQueryと比較した場合の、Vue.jsの大きく違うポイントは次の通りです。
199+
200+ * Vueは特定のノード以下のみをプログラムで変更可能にする
201+ * 1つのHTMLを元にページ切り替えを実現できる(シングルページアプリケーション)
202+
203+ 元のjQueryでシングルページアプリケーションを実現しているケースはほとんどないと思うので、いったん、元のプロジェクトは複数ページから構成されているものとして話を進めます。
204+
205+ まず、複数のページであっても、1つのHTMLとTypeScriptコードで実現します。今まで共通のヘッダーなどを個別に実装していた場合はベースのHTML側に書いておけば共通で利用されます。複数のページで違いの発生する部分のみを.vueファイルにします。
206+
207+ まずはjQueryをライブラリに追加します。
208+
209+ .. code-block :: bash
210+
211+ $ npm install jquery @types/jquery
212+
213+ 次に、.vueファイルを作成します。これはHTML、CSS、スクリプトが1ファイルにまとまった、シングルファイルコンポーネントと呼ばれるものです。\ ``<template> ``\ にHTMLを書き、\ ``<script lang="ts"> ``\ にはTypeScriptのコードを書きます。
214+
215+ DOM読み込み後に呼ばれるイベントハンドラでjQueryのイベント定義などを行っていました。jQueryの読み込み後のハンドラーは次のどちらかを設定していました。
216+
217+ * ``$(document).ready( function () {}) ``
218+ * ``$(function() { }) ``
219+
220+ Vue.jsでは、ページごとにコンポーネントと呼ばれるオブジェクトを作成します。それが作成されるタイミングで\ ``setup() ``\ メソッドが呼ばれますが、その中で\ ``onMounted() ``\ 関数に登録したコードが実行時に呼ばれます。まずはそこにjQueryのコードを移植してしまいましょう。完成形は次の通りです。
221+
222+ .. code-block :: html
223+ :caption: /src/views/mypage.vue
224+
225+ <template >
226+ <div >jQuery test page</div >
227+ <button class =" pushbutton" >button</button >
228+ <div class =" panel" style =" display : none ; background-color : lightblue ;" >
229+ hidden
230+ </div >
231+ </template >
232+
233+ <style scoped >
234+ .clickedButton {
235+ background-color : " yellow" ;
236+ }
237+ </style >
238+
239+ <script lang =" ts" >
240+ import $ from " jquery" ;
241+
242+ import { defineComponent , onMounted } from " vue" ;
243+
244+ export default defineComponent ({
245+ name: " MyPage" ,
246+ setup () {
247+ onMounted (() => {
248+ $ (" .pushbutton" ).click (() => {
249+ $ (" .pushbutton" ).css (" background-color" , " yellow" );
250+ $ (" .panel" ).fadeIn ();
251+ });
252+ });
253+ }
254+ });
255+ </script >
256+
257+ なお、この\ ``onMounted ``\ は作成時に一度だけ呼ばれます。その後、属性の変更などがあると、タグの再作成プロセスが走ります。差分のみの更新のはずですが、元の変更によってはイベントハンドラをつけたタグが再作成されたりすることもあり、イベントハンドラがなくなる可能性がもあります。このやり方はあくまでも移行のための暫定なので、本番環境にデプロイなどをしてはいけません。
258+
259+ ページを作ったら、そのファイルが表示可能になるように、ルーターに登録します。そのページのURLと、その時に使われるコンポーネントのペアを関連づけるものです。これで表示できるようになりました。
260+
261+ .. code-block :: ts
262+ :caption: /src/router/router.ts
263+
264+ import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
265+ import MyPage from "../views/mypage.vue";
266+
267+ const routes: Array<RouteRecordRaw> = [
268+ {
269+ path: "/",
270+ name: "MyPage",
271+ component: MyPage
272+ },
273+ ];
274+
275+ const router = createRouter({
276+ history: createWebHistory(process.env.BASE_URL),
277+ routes
278+ });
279+
280+ export default router;
281+
282+ まずはここまでで動作することを確認しましょう。
283+
284+ クリックイベントの定義
285+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286+
287+ Vueの作法でクリックイベントを設定しましょう。jQueryはHTMLの外でタグとイベントん関連付けを\ ``$.click() ``\ を使って行っていました。Vue.jsの場合は、テンプレートの中で関数を設定します。呼び出す関数は\ ``setup ``\ の中で作成します。テンプレートで\ ``clicked ``\ という名前で呼びたい場合には次のように作成します。
288+
289+ .. code-block :: ts
290+ :caption: /src/views/mypage.vue
291+
292+ import { defineComponent } from "vue";
293+
294+ export default defineComponent({
295+ name: "MyPage",
296+ setup() {
297+ // イベントハンドラを作成
298+ const clicked = () => {
299+ $(".pushbutton").css("background-color", "yellow");
300+ $(".panel").fadeIn();
301+ }
302+ // テンプレートで使う要素はsetupのレスポンスのオブジェクトとして返す
303+ return {
304+ clicked
305+ }
306+ }
307+ });
308+
309+ HTMLは次のようになります。素のHTMLのイベントハンドラの方に近くなります。ここまで動作することを確認しましょう。
310+
311+ .. code-block :: html
312+ :caption: /src/views/mypage.vue
313+
314+ <button class =" pushbutton" @click =" clicked" >button</button >
315+
316+ jQueryとの大きな違いとしては、変更対象のHTMLタグの発見方法があります。jQueryはすでにあるHTMLのタグの中から変更対象を見つける必要があります。そのため、セレクターという機能に磨きをかけて、変更対象をすばやく確実に見つける機能を備えました。一方のVue.jsは、生成時に差し込みます。そのため、タグを探してくる「セレクター」という概念がありません。
317+
318+ クリックによるインタラクションをVue.js化
319+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
320+
321+ Vueでは一元管理されたデータを元にビューを更新するとすでに説明しました。その本丸に攻め入ります。その状態管理で利用するのが\ ``reactive ``\ 関数です。引数にオブジェクトを渡すと、それがコンポーネントのデータ源として利用できます。これもテンプレートから利用するため、\ ``setup() ``\ の返り値で返します。
322+
323+ 今回のサンプルは小さいので問題ありませんが、実用的なコンポーネントの場合、この\ ``reactive() ``\ の各要素にコメントを書いておいても良いでしょう。
324+
325+ ``clicked() ``\ の中は、単にこの状態を更新するだけになりました。
326+
327+ .. code-block :: ts
328+ :caption: /src/views/mypage.vue
329+
330+ import { defineComponent, reactive } from "vue";
331+
332+ export default defineComponent({
333+ name: "jQuery",
334+ setup() {
335+ const state = reactive({
336+ show: false // パネルの表示制御に使う
337+ });
338+
339+ const clicked = () => {
340+ state.show = true;
341+ };
342+ return {
343+ clicked,
344+ state
345+ };
346+ }
347+ });
348+
349+ テンプレート側はつぎのようになりました。この状態をみて、ボタンの方はクラスのON/OFFの切り替えを行い、パネルの方はCSSのスタイルの内容の変更を行っています。
350+
351+ ``:class ``\ は、オリジナルのVueのルールでは\ ``v-bind:class ``\ でしたが、省略記法として\ ``:class ``\ も提供されています。実は、\ ``@click ``\ も\ ``v-on:click ``\ の省略形になります。
352+
353+ .. code-block :: html
354+ :caption: /src/views/mypage.vue
355+
356+ <button
357+ @click =" clicked"
358+ :class =" { clickedButton: state.show }"
359+ >
360+ button
361+ </button >
362+ <div class =" panel" :style =" {visibility: state.show ? 'visible' : 'hidden', backgroundColor: 'lightblue'}" >
363+ hidden
364+ </div >
365+
366+ 単に表示のON/OFFを切り替えるだけであれば、\ ``v-if ``\ や\ ``v-show ``\ といったディレクティブもあります\ [# ]_\ 。
367+
368+ .. [# ] https://v1-jp.vuejs.org/guide/conditional.html
369+
370+ フェードイン効果を実現する
371+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
372+
373+ クリックしたタイミングでスタイルシートや表示のON/OFFの切り替え方法は学びました。本節のラストとして、フェードインを実現します。
374+
375+ jQueryのフェードインはふわっと表示させるときに利用します。デフォルト値は0.4秒での表示になります。また、透明度の変化のカーブの選択もできます。このあたりの複雑なパラメータの時間変化は、最新のCSSを使えば造作もありません。まずはフェードインのCSSクラスです。\ ``opacity ``\ と\ ``visibility ``\ が非表示状態で開始します。その後、クリックで追加のクラス設定があると最終的には完全表示になりますが、その途中経過で0.4秒かけて表示するための\ ``transition ``\ が含まれています。
376+
377+ .. code-block :: css
378+ :caption: /src/views/mypage.vue
379+
380+ .hiddenPanel {
381+ opacity : 0 ;
382+ visibility : hidden ;
383+ }
384+
385+ .hiddenPanel.fadeIn {
386+ opacity : 1 ;
387+ visibility : visible ;
388+ transition : opacity 0.4s , visibility 0.4s ;
389+ }
390+
391+ すでに実装されているロジックで問題ありません。残る修正箇所はテンプレートのみです。
392+
393+ .. code-block :: html
394+ :caption: /src/views/mypage.vue
395+
396+ <div
397+ class =" panel" :class =" {hiddenPanel: true, fadeIn: state.show }"
398+ :style =" { backgroundColor: 'lightblue' }" >
399+ hidden
400+ </div >
401+
402+ これで、クリックされた瞬間に\ ``fadeIn ``\ クラスが付与されるようになりました。このCSSクラスは0.4秒かけて切り替えると実装されているため、\ ``jQuery ``\ の\ ``fadeIn ``\ 同等の処理が実現できました。
403+
404+ Vue.jsの方が行数が長くなる?
405+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
406+
407+ jQueryの移植の基本について説明してきました。イベントのハンドリングとスタイルの切り替えはこれで問題がなくなったでしょう。
408+
409+ これらの処理はjQueryがもっとも得意とするものであり、Vue.jsの方がかなり行数が増えてしまっています。ここだけみるとVue.jsのメリットが少なく感じます。
410+
411+ しかし、Ajax的なロジックの実装を開始しはじめると複雑度は逆転します。サーバー通信して、取得してきたJSONを元にリストを表示するようなケースでは、jQueryの場合はプログラムの中でHTMLのDOM要素を作って挿入という処理になります。適切なHTML片を作って挿入するのは大変ですし、雑に実装するとXSSというセキュリティの穴があく恐れもでてくるため、jQueryでも\ `mustache <http://mustache.github.io/>`_\ や\ `underscore.jsのtemplate <http://underscorejs.org/#template>`_\ 、\ `handlebars <https://handlebarsjs.com/>`_\ といったテンプレートエンジンの導入を検討し始めるところです。ロジックと表示の元データと表示のテンプレートが分離しにくいといった問題も出てきますし、コードの行数も爆発し始めます。表示以外に、編集や削除をしたいといった要件が出てきたらもうお手上げでしょう。
412+
413+ Vue.jsの場合はスクリプトはデータの保持のみになりますし、テンプレートによる結果のレンダリングは最初から行っていますので、複雑度は変わりません。\ `v-for <https://v3.vuejs.org/guide/list.html#mapping-an-array-to-elements-with-v-for >`_\ ディレクティブでリストの表示も簡単です。
414+
415+ それ以外にjQueryの方が短く、コードが長くなってしまうケースとしては、jQueryプラグインを活用している場合があります。jQueryにはjQuery UIも含めた高度なコンポーネントの部品が数多くあります。これらはとても便利ですが、やはりデータと表示の分離がしにくく、うまくレールに乗っかれるような最速のパターンでは短かったとしても、例えばカレンダー部品で取得した情報を元にバリデーションを行って結果を画面に反映させたり、インタラクションが増えてくると周辺のコードが複雑化しがちです。
416+
417+ まだVue.js 3には対応していませんが、\ `bootstrap-vue <https://bootstrap-vue.org/ >`_\ という人気のあるBootstrapをVue.js用にコンポーネント化したものもあります。複雑なフィルタリング機能などを実現するテーブルコンポーネントも\ `cheetahgrid <https://future-architect.github.io/cheetah-grid/#/ >`_\ などのよりよい代替部品がすぐに見つかるでしょう。自分でコンポーネントを実装しなければならない場合を除けば、そこまで行数も変わらず実現できるでしょう。
418+
419+ まとめ
420+ -----------------
421+
422+ Vue.jsについて紹介してきました。まずはVueの使い勝手の良さの源泉であるvue-cliによるプロジェクト作成の方法について紹介しました。TypeScriptフレンドリーな書き方については2系で相性が良いクラス形式の実装方法と、3系のcomposition APIの2種類紹介しました。
423+
424+ せめて、2系にバックポートされた3系 APIを実現するプラグインの\ `@vue/composition-api <https://github.com/vuejs/composition-api >`_\ のバージョンからベータが外れると、プロダクション開発もすべてcomposition APIを利用する方針で決定できると思いますが、現時点ではどちらを使うかはプロジェクトの寿命やリリース時期を勘案してどちらか決める必要があるかもしれません。
425+
426+ そのあとで、よくありそうなユースケースとして、jQueryで書かれたウェブフロントエンドをVue.jsで置き換える方法について紹介しました。一見すると行数が増えますが、ロジックが増えても複雑度が増えていかないので、のちのちにありがたみを感じやすくなるでしょう。
0 commit comments