目的
プログラミングはしたことあるけど、Web系はあまり経験していない。
Webプログラミングしたことあるけどググって何となくやっている。
上記のような人たちを対象に Vue.js でアプリケーションを書く利点を理解してもらう。
加えて composition-api を利用する利点を理解できるまでにしたい。
基本的な使い方やAPIなどは世の中に星の数ほどあるチュートリアル記事に任せるとして、ここでは、なぜ Vue をWeb開発に利用するのか?という点にフォーカスする。
注意
- プログラミング経験自体はあることを前提とするので、基本的な制御構文や個別のAPIを掘り下げたり詳細な説明はしない。
- 説明の順序的にHTMLなどの説明からすることになるが、個別のタグやCSSのプロパティについての説明もしない。
- React などと比較して Vue を選択する動機などは説明しない。本文章の役割はその前段階にある。
- React を勧めたい人はこの説明をそのまま React や Hooks に変えればよいのかもしれない
- Vue.js にはカスタムコンポーネントの定義という強力な機能があるが、本文章ではそれに触れない。別途 WebComponents と比較した記事を書くことを予定している
- インタラクティブなWebアプリケーションを作るときにVue.jsを利用する動機を理解してもらうことにフォーカスし、トピックを絞りたいのでモジュールバンドラーなどには触れない
本文章の構成
初めにHTML/CSSの簡単な説明をする。続いてインタラクティブなアプリケーションを実現するためにJavascriptが必要であることを示す。
その際にはいくつかのAPIを説明しつつ、どのようにインタラクティブなWebアプリケーションを作成するかの説明をする。
そのあとは、良いプログラムはどのようなものか?という点にフォーカスしていく。
なぜ、DOMエレメントの操作を中心としたプログラミングはつらくなるのか?どうすればその負担を軽減できるかを考える。
ここまでの操作中心のプログラミングに対する問題への一つの回答として、Vue.jsを紹介し宣言的にUIが記述できることの利点を示す。
実際に VanillaJS と Vue.js でアプリケーションを作成し、宣言的にUIが記述できることの強みを体感する。
最後は、テスタビリティなどの観点から Vue.js の object-based API の問題点を示す。そして composition-api がその問題点に対する一つの解決策であることを紹介する。
Webコンテンツと HTML / CSS について
HTMLとCSSに関する詳細な説明は以下を参照するとよい。
以下に比較的簡単な説明を記載する。
HTML
HTMLはHyperText Markup Languageを略した語。HyperText とは文書と文書をリンクする特徴を持った文書を指す。
Markup Languageとは、文書に対して意味を与えることを目的とした言語を指す。
例えば HTMLには文書の見出しを示す タグ
として h1
, h2
, h3
などがある。
これは <h1>文書の目的</h1>
のように利用し、h1タグで囲まれたテキストがレベル1の見出しとして意味を持つことを表している。以降ではタグで囲んでいる領域を「要素」や「エレメント」と呼ぶ。例えばh1要素などと呼ぶ。
HyperText としての特徴である文書のリンクは a
タグで実現する。
<a href="google.com">Google.com へのリンク</a>
ここで、 href
はタグの属性(attribute)を示している。
href属性はhypertext referenceを略したもの(諸説あるらしい)で設定された値はリンクする値を示す。
つまり、「Google.com へのリンク」というテキストは、"google.com"にある別のhypertextへのリンクであることを示す。
ブラウザー
HTMLは文書に意味付けをするが、そのまま読むのは難しい。ブラウザを利用することで意味に応じた見た目で文書を表示してくれる。
例えば h1 タグなどの見出しは他の文字よりも大きく・太く表示される。
a タグはマウスカーソルを合わせるとクリックできるような表示に変化する。クリックすることでリンク先の文書が表示される。
HTMLは意味づけされた文書だが、ブラウザーを通すことで(原始的だが)インタラクティブな体験ができる。
CSS
CSSはCascading Style Sheetsを略した語。ブラウザーの表示機能を上書きする機能をもつ。
このようにブラウザで定義されている基本的な表示(Style)を引き継ぎつつ部分的に上書きされていくさまからCascadingという語が与えられている。
CSSは以下のような基本構文を持つ。
<セレクター> {
<プロパティ>: <値>
}
セレクターはエレメントを指定する。プロパティは変更したいスタイルを示す。値はスタイルの値を示す。
例えば以下は p 要素の文字列を赤色にするルールを定義している。
p {
color: red;
}
セレクターにはタグ名を指定する以外にいくつか指定方法がある。
比較的単純な指定方法として id と class による指定を説明する。
これらはいずれもHTMLの属性として指定できる。
以下のHTMLに対して
<p>huga<p>
<p id="hoge">hoge</p>
<p class="foo">foo</p>
<p class="foo bar">foobar</p>
以下のCSSを設定したとき
#hoge {
color: red;
}
.foo {
font-weight: bold;
}
.bar {
color: blue;
}
以下のように表示される
- hugaはそのまま
- hogeは赤字
- fooは太文字
- foobarは太字で青文字
この章のまとめ
- HTMLは文書に意味を設定するための言語
- ブラウザで見ることでHTMLは意味に応じた表示になる
- ブラウザを用いることでHTMLでインタラクティブな体験ができる
- CSSはブラウザでの表示を変更することができる
プラクティス
- HTMLのタグを調べてHTML文書を作成し、ブラウザでその表示を確認せよ
- CSSのセレクターとプロパティについて調べ、1で作成した文書をスタイリングせよ
JavaScript
JavaScript はブラウザー上で動作するプログラミング言語。
他の言語と同様に一般的な計算などができる。
しかし、主な興味は静的な文書という性質のWebコンテンツをより動的でインタラクティブなアプリケーションへと引き上げることにある。
インタラクティブな体験に必要な要素は大きく分けて以下の二点となる。
- ユーザの入力を受け付けること
- 画面を更新すること
つまり、インタラクティブとは相互的であるということなので、ユーザの入力に応答して(前者)入力に対して応答すれば良い(後者)
本章の説明には以下のHTMLを利用する
<main>
<h1>Counter</h1>
<p id="count">Count = 0</p>
<button id="btn">Click</button>
</main>
DOM
WebのコンテンツはHTMLなので、動的なコンテンツとするためにはJavascriptでHTMLを操作できる必要がある。
HTML文書は要素をツリー状に表現したDOM (Document Object Model)、特にHTML DOMという仕様を利用してJavaScriptからHTMLを操作することができる。
ユーザの入力を受け付ける
ユーザの入力を受け付けるには JavaScriptを使ってHTMLの要素を取得する必要がある。
以下のようにしてDOM要素を取得できる。
const btnElement = document.querySelector("#btn")
document
オブジェクトはブラウザ環境でグローバルに存在し、DOMにアクセスするためのAPIを提供している。
querySelector
メソッドは引数にCSSセレクターをうけとり、セレクターに合致する最初に見つかった要素を返す。
https://developer.mozilla.org/ja/docs/Web/API/Document/querySelector
ここでは、ユーザーからの入力として、ボタンへのクリックに応答するようにする。
クリックに限らず発生するイベントは addEventListner
メソッドで応答できる。
https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener
より正確に言えばには特定のイベントが発生したときに、実行してほしい処理を登録することができる。
以下のように処理を登録する。
btnElement.addEventListener('click', handlerFunction);
handlerFunction
は実行する処理が定義された関数を示している。
例えば以下のように定義する。
また、console.log
はブラウザの開発者ツールに引数を表示するメソッド。
const handlerFunction = ()=> {
console.log("hoge")
}
ここでは定義した関数を変数handlerFunctionへ代入している。
ここで、JavaScriptにおいて関数は、文字列や数値と同様に扱うことができることに注意する。
このような特徴があるため、addEventListner
メソッドへの引数として handlerFunction
を渡すことができる。
ここまでで、以下のようなコードになっている。
See the Pen JS Step1 by sterashima78 (@sterashima78) on CodePen.
開発者ツールを開いてボタンをクリックするとhoge という文字が表示されることが確認できる。
ここまでで、ユーザの入力に応答することができるようになった。
画面を更新する
ここまでに記載したように画面はHTMLとCSSで指定をもとにブラウザによって構成される。
したがって画面を更新するということはHTML (DOM) やCSSのスタイル定義をJavascriptで変更するということになる。
ここでは、ボタンをクリックしたら、これまでにクリックした回数を表示するように画面を更新させるように変更する。
まずは更新したいエレメントを取得する。
const countElement = document.querySelector("#count")
innerText
属性は、対象エレメントがマークアップしているテキストを有している。
// countTextには "Count = 0" が含まれる
const countText = countElement.innerText;
この文字列から数値部分を抜き出して、1を加算し、innerText を更新すれば期待通りの動作をする。
// numberTextの中身は "0"
const numberText = countText.replace("Count = ", "")
// 文字列 "0" を数値に変換して
const count = parseInt(numberText)
// 先程得た数値に 1 加えた数と "Count = " という文字列を結合して、
// "Count = 1" という文字列にしている
// それを `innerText` 属性へ代入している
countElement.innerText = `Count = ${count + 1}`
ここまでが以下になる。
See the Pen JS Step2 by sterashima78 (@sterashima78) on CodePen.
ボタンをクリックすると数字部分がカウントアップされているのが確認できると思う。
今回は、テキスト属性であったが、同様にスタイルやその他の属性を操作するための方法や、別のDOMを追加・削除する方法が提供されており、それを利用することで画面の更新ができる。
まとめ
- ブラウザでは HTML 文書は DOM として解釈・保持される
- JavaScriptを利用することで DOM を操作することができる
- DOM へのイベントに応答と、DOMの変更による画面の更新をJavaScriptで行うことでインタラクティブなWebアプリケーションが実現できる
プラクティス
- DOM で発生するイベントについて調べよ
- DOM の属性や、それを変更するAPIについて調べよ
- 1 で調べたイベントに応答し、2で調べたAPIを利用して任意のDOM属性が変更されるようなインタラクティブなアプリケーションを作れ
JavaScriptによるアプリケーション作成で発生する問題
前章までで、基本的なWebアプリケーションを作る仕組みを説明した。
本章では、前章までの延長線でアプリケーションを構築するとぶつかる問題点について考える。
この章では、以下のコードを例にする。
See the Pen JS Step3 by sterashima78 (@sterashima78) on CodePen.
このコードでは、ボタンのテキストにボタンをクリックした回数が表示され、クリックするごとに回数が増えていく。
問題点: 表示が「操作の積み重ね」によって決定される
大きな問題として、画面への表示が操作の積み重ねによって決定することが挙げられる。
このアプリケーションでいうと、ボタンのテキスト部分は「クリック」という操作によって決定する点。
もちろんこれぐらい小さなアプリケーションであれば、大きな問題にはならないことが多い。
ここでは、たくさんクリックをしたら表示が崩れる。という報告があったとする。
明らかに数字の桁数が長いことが問題なので、上限を決めるなりすれば良いことがわかる。
しかし、アプリケーションの複雑さによっては、どのような操作によってこの状況を再現できるかわからないこともある。そうなれば、たくさんクリックをするしかない。
改善の方針
堅牢なソフトウェア作るときに気をつけたら良いことに、「責任を適切に分ける」ことがある。
例えばこのアプリケーションには、以下のような責任の要素がある。
- ボタンの表示 (見た目)
- ボタンがクリックされた回数 (状態)
- ボタンのクリック検知 (イベントへの応答)
- クリック回数の更新 (状態の変更)
- 表示の更新 (状態に基づく DOM の更新)
まずは、これらが HTML/CSS/JavaScript のうちどれの責任になっているか確認する。
- ボタンの表示 (見た目) : HTML/CSS
- ボタンがクリックされた回数 (状態): HTML
- ボタンのクリック検知 (イベントへの応答): JavaScript
- クリック回数の更新 (状態の変更): JavaScript
- 表示の更新 (状態に基づく DOM の更新): JavaScript
「見た目」と「状態」の責任を HTML が担っていることが問題といえる。
つまり、アプリケーションの状態がその時の見た目としてだけ保持されているため、再現が難しくなる。
これは、先程提起した問題の直接的な原因のひとつ。
状態と表示の責任が分離してあり、状態によって表示が決定するようになっていれば、この問題は解決できる。
なぜなら、状態が適当であるのに、表示が崩れていればそれは「表示」の問題とわかる(スタイルの誤りなど)。逆に状態で不適当であれば、「状態の変更」に問題があるとわかる(意図していない値を設定してしまうなど)。
改善のために、まず、これらの責任を適切な言語に分けることが必要。
加えて、プログラムコード上でもさらに責任を分離することで検証などがやりやすくなる。
改善の実施
この章のはじめに記載したコードからスタートし、段階的にすすめる。
See the Pen JS Step3 by sterashima78 (@sterashima78) on CodePen.
状態を定義する
表示である、DOMから値の取得・変更を直接することをやめる。
代わりにJavaScriptに状態を定義し、これを変更し、この値をDOMへ反映させるようにする。
以下のように修正した。
See the Pen JS Step3-1 by sterashima78 (@sterashima78) on CodePen.
状態の変更を分離する
状態を変更する処理を分離する。
例えば以下のようになる。
See the Pen JS Step3-2 by sterashima78 (@sterashima78) on CodePen.
状態の変更を分離することには大きなメリットがある。
それは動作の検証がしやすくなることだ。
特に、今回作成したコードでは変更関数が依存している状態を、関数の引数として注入するようにしている。
これによって、検証プログラムでは任意の値を注入することができる。
たとえば、バグ報告を受けて、1000000 以上にはインクリメントできない(増えない)ような仕様が追加されたとしよう。
これを検証するためのコードは以下のように記述できる。
/**
* インクリメントされる (1 -> 2)
*/
const state1 = {
count: 1
}
mutator.increment(state1)
if(state1.count == 2) {
counsole.log("OK: test increment1")
} else {
throw new Error("Fail: test increment1");
}
/**
* インクリメントされない (1 -> 2)
*/
const state999999 = {
count: 999999
}
mutator.increment(state999999)
if(state999999.count == 999999) {
counsole.log("OK: test increment999999")
} else {
throw new Error("Fail: test increment999999");
}
表示の更新を分離する
表示の更新も同じ要領で分離する。
状態の変更のときは依存している状態を引数で注入できるようにしたので、
ここでは、更新のもととなる状態と、更新対象のDOMを注入する。
例えば以下のようになる。
See the Pen JS Step3-3 by sterashima78 (@sterashima78) on CodePen.
こちらも同様に、責任を単一にし、依存を注入することで検証がやりやすくなる。
イベントの登録を分離する
考え方はここまでと同じなので、完成したものを見てもらえば、理解できると思う。
See the Pen JS Step3-4 by sterashima78 (@sterashima78) on CodePen.
再利用性
ここまで、責任を分離することに注力してきたが、これによって更に良いことがある。
それは、再利用ができる小さな機能の集合でアプリケーションが構成されたことだ。
例えば、状態が分離されたことで、新しい状態を用意すれば簡単に別のカウンターボタンを用意できる。
See the Pen JS Step3-5 by sterashima78 (@sterashima78) on CodePen.
render を変更すれば表示のされ方を変えることができる。
See the Pen JS Step3-6 by sterashima78 (@sterashima78) on CodePen.
まとめ
- DOM の変更をベース作成したアプリケーションが「表示」と「状態」の責任が分離できず、問題の切り分けなどがしにくい
- 責任を分離したコードは再利用性が高く、テストがしやすい
プラクティス
- 前章で作成したアプリケーションを本章の方針に沿ってリファクタリングせよ
宣言的なプログラミング
プログラミングのスタイルには大きく分けて命令的なものと宣言的なものがある。
この章では、まず命令的なスタイルと宣言的なスタイルについて説明する。
その上で、Webアプリケーションの構築において宣言的なスタイルが有効であることを示し、Vue.js を利用することでこれが実現できることを示す。
「命令的」と「宣言的」
ここでは、命令的のことを、ある目的を実現するための処理を逐次的に記述するスタイルのことを指す。
その一方で、宣言的のことは、ある目的のあるべき状態を記述するスタイルのことを指す。
具体的な例を出すと、HTML/CSS は宣言的。
赤文字で、文字サイズが 15px の文章、「赤文字テキストです。」を表示したいとする。
以下のように、表示したい文言や、そのスタイルを記述することで実現できる。
<p style="color:red;font-size:15px;">赤文字テキストです。</p>
これと同等なことが JavaScript でもできる。
まず、document.createElemnt
という API で DOM が作れる。
そして、document.createTextNode
という API で文字列を表す DOM の要素が作れる。
加えて <DOMElement>.appendChild
という API を利用すると、 <DOMElement>
の小要素として、別の DOMElement を追加できる。
最後にスタイル属性は style
というプロパティで DOM は有している。
上記を踏まえると、以下のように処理を記述することで同等な表示に相当するDOMを得ることができる。
// 文章を表す要素を作成する
const p = document.createElemnt("p")
// テキスト要素を作る
const text = document.createTextNode("赤文字テキストです。")
// テキスト要素を文章要素に挿入する
p.appendChild(text)
// スタイル属性を設定する
p.style["font-size"] = "15px"
p.style["color"] = "red"
この例では、JavaScript はDOMを直接操作している一方で、HTMLの例ではブラウザーがDOMを組み立ててくれている。HTMLは宣言的にどのような構造の文書であるかをそのまま記述すればいい。
HTMLのような、宣言的スタイルは誤りが起こりにくい。
JavaScript でも宣言的なスタイルでプログラミングはできる。
以下で簡単な例を示す。
1から5までが入った配列の各要素を二乗にした配列を作成したいとする。
はじめに、手続き的な例を示す。
// もとの配列
const query = [1,2,3,4,5]
// 結果の配列
const squared = []
// 1つ目の要素を二乗にする
squared[0] = query[0] * query[0]
// 2つ目の要素を二乗にする
squared[1] = query[1] * query[1]
// 3つ目の要素を二乗にする
squared[2] = query[2] * query[2]
// 4つ目の要素を二乗にする
squared[3] = query[3] * query[3]
// 5つ目の要素を二乗にする
squared[4] = query[4] * query[4]
上記の例は冗長な記述であるから手続き的であるという言うわけではない。
以下のように記述しても一つづつ要素を二乗し、新しい配列に詰めていくという命令を記述していることに変わりない。
// もとの配列
const query = [1,2,3,4,5]
// 結果の配列
const squared = []
for (item in query) {
squared.push(item * item)
}
宣言的な記述にすると以下のようになる。
// もとの配列
const query = [1,2,3,4,5]
// 二乗を定義
const square = (item) => {
return item * item
}
// squared は query の square (二乗) であるという宣言
const squared = query.map(square)
上記の例では、まず『二乗自体の定義』を行っている。
次に、『squaredはqueryの各要素を二乗にしたもの』という宣言をしている。
これまでの説明に出てきたような『操作の積み重ね』によって結果が得られる命令的なスタイルは、「どうやって値を得るか」を記述するが、この例のような宣言的なスタイルは「どんな値か」に注目して記述するため理解しやすく間違いが起きにくい。
Vue.js
Vue.js (https://jp.vuejs.org/index.html) は Webアプリケーションにおいて View に相当するレイヤーを担当するライブラリー。
前の章で、責任の分離の例を示したが、そこにおける表示の更新などをやってくれる。
この仕組みを利用することで、開発者は状態の変更などのロジックに集中できる。
簡単な例を示すために前の章で作成したカウンターボタンを Vue.js を使って記述してみる。
See the Pen JS Step3-4-Vue by sterashima78 (@sterashima78) on CodePen.
HTMLに特別な属性を付与することで状態と表示やイベントと処理の関連付けができる。
ここでは v-text
と @click
と使っている。
v-text
は状態を対象DOMのテキストの表示に関連付けしている。状態が変われば Vue によって表示の更新をしてくれる。
@click
はクリックイベントと処理を関連付けしている。ここでは、クリックをすれば increment
が呼ばれて状態が更新される。
このようにHTMLでの宣言によってイベントの登録・UI更新が実現できる。
他にも Vue には宣言的にプログラミングをするための機能がある。
例えば、ボタンに表示するテキストを 「3 回」のような文言に変更したいとする。
Vue.js の機能を効果的に使うと以下のように修正して実現できる。
See the Pen JS Step3-4-Vue-1 by sterashima78 (@sterashima78) on CodePen.
computed
という新しい属性が追加されている。computed
は data
つまり、アプリケーションの状態から算出される別の値を定義する。
ここでは、アプリケーションの状態である count
から算出されるボタンに表示するテキスト btnLabel
を定義している。この値は count
が更新されると自動的に更新される。
開発者は btnLabel
とはどのような値かを宣言するだけでよく、値や表示の更新などを考慮しなくていい。
まとめ
- 命令的なスタイルは結果が「操作の積み重ね」によって得られるので比較的誤りが発生しやすい
- 宣言的なスタイルはある値の定義を中心に記述するため比較的誤りが発生しにくい
- Vue.js は状態の変更に伴う表示の更新を行ってくれる
- Vue.js は状態から算出される別の値を定義することで、それらの更新を自動で行ってくれる。これを利用することで宣言的なプログラミングがしやすくなる
プラクティス
- 前章で作成したサンプルアプリケーションを Vue.js を用いて実装せよ
サンプルアプリケーションの作成・比較
ここまで、Webアプリケーションを構築する上で、状態と表示を分離させることや、宣言的なプログラミングスタイルが有効であることを説明してきた。
そして Vue.js がこれらを考える上で有効であることを紹介した。
ここでは、以下の三種類の簡単なTODOアプリケーションを示す。
- 責任が分離できてないアプリケーション
- 責任を分離したアプリケーション
- Vue.jsを用いて責任を分離したアプリケーション
これらの例を比較することで、ここまで説明したことの効果を実感するとともに Vue.js を利用することで比較的簡単に実現できることを示す。
責任の分離を意識しない例
仕様を考えながら作っているとこんな感じになりがちなのではないか。
これくらいのサイズであれば、上から順番に読んで行けばわからないこともないがそれなりにストレスが貯まると思う。
余力があれば章末の「プラクティス」を実施し、この状態のコードの拡張を試みてほしい。
(レガシーなシステムであれば平気でこれくらいのコードが製品として今も可動していることも多い)
See the Pen 責任分離をしないTODO by sterashima78 (@sterashima78) on CodePen.
責任の分離を意識した例
責任の分離を意識した例を以下に示す。
ただし、パフォーマンスなどの面で問題があるため、プロダクトコードとしては利用を避けるのが無難と考えられる。
パフォーマンスについて、ここでは説明しない。
見通しの良さなどもあるが、単体テストを書こうとしたときの書きやすさが段違いに変わってくるので、そのあたりも考えてみてほしい。
See the Pen 責任分離をしたTODO by sterashima78 (@sterashima78) on CodePen.
責任の分離を意識しVue.jsを用いた例
記述がより簡潔になったことが理解できると思う。
これは、値の監視と状態の更新、そして、状態の更新と表示の更新を Vue が吸収してくれているため。
なお、Vue.js の機能を利用すればより簡潔な記述も可能だが、あえて対比しやすい記述にしている。
See the Pen 責任分離をして Vue.js を使った TODO by sterashima78 (@sterashima78) on CodePen.
まとめ
- ある程度複雑なアプリケーションでも責任の分離を意識することでより保守性の高いソフトウェアになる
- この点については、何を使うかではなく、どう考え、どう作るかが大切
- Vue.js を利用することで、値の更新など命令的に記述しなくてはならない箇所が吸収され、より宣言的にプログラミングができる
プラクティス
- 「責任の分離を意識しない例」, 「責任の分離を意識した例」, 「責任の分離を意識しVue.jsを用いた例」 それぞれに対して以下の機能を追加し、拡張の容易性などを比較せよ
- 特定のTODOを削除するボタン
- 完了した (isDone == true) TODO をすべて削除するボタン
- 全TODO表示と未完了TODOのみ表示を切り替えるようなチェックボックス
Vue.js を利用することで発生する問題点
何事にも問題点はあり、Vue.js についても当然いくつかあるのだが、ここではわかりやすい一点についてフォーカスしたい。
問題点: "this" への依存
責任の分離について説明した章では、それぞれの責任が依存するもの(状態など)は関数の引数として注入できるようにしていた。
これによってテストが容易になったり、再利用や拡張が簡単になるという利点を説明した。
しかし、Vue.js は状態や処理は JavaScript の特別なオブジェクト this
を経由して行われている。これによって試験などが難しくなっている。
この点について詳細な説明は避けるが、テストのときには this で参照される値を適切に変更することが必要になる。
以下は、先の例に出てきた increment
, btnLabel
のテストコードの例。options
が Vue
コンストラクタに渡していたオブジェクトだと思ってほしい。
/**
* increment の試験
* インクリメントされる (1 -> 2)
*/
const state1 = {
count: 1
}
options.methods.increment.bind(state1).call()
if(state1.count == 2) {
counsole.log("OK: test increment1")
} else {
throw new Error("Fail: test increment1");
}
/**
* btnLabel の試験
* 回 という文字が付与される
*/
const state10 = {
count: 10
}
const label = options.computed.btnLabel.bind(state10).call()
if(label == "10 回") {
counsole.log("OK: test btnLabel")
} else {
throw new Error("Fail: test btnLabel");
}
Vue.js によって宣言的にプログラミングをすることができるようになったが、状態や処理が this
を介して密に結合するようになってしまった。
composition-api
composition-api (https://vue-composition-api-rfc.netlify.com/) は 2020の1Qにリリース予定となっている新しいバージョンの Vue で採用予定のAPI。現在はプラグインとして提供されており、既に利用することができる。
composition-api を利用することで、this
に依存することを回避することができる。また、関連する状態や処理をひとまとまりに定義することができ、試験や再利用がしやすくなる。
以下は、前の章で作成したTODOアプリケーションを composition-api を用いて実装したもの。
See the Pen 責任分離をして Vue.js と composition-api を使った TODO by sterashima78 (@sterashima78) on CodePen.
ここでは、新しいTODOを追加するためのフォームとTODOに関連する状態や操作をそれぞれ useTextForm
と useTodo
に分離した。
状態とそれを変更する処理が関数に分離されたので、テストがしやすくなっている。
また、依存する値があれば引数で注入するようになっているので、これもテストの容易性を高めることに寄与している。
例えば、 useTextForm
の試験は以下のように書ける。
const {input, isInputted, clear} = useTextForm("init")
if(input.value == "init") {
console.log("OK: init")
} else {
throw new Error("Fail: init")
}
if(isInputted.value) {
console.log("OK: isInputted")
} else {
throw new Error("Fail: isInputted")
}
clear()
if(isInputted.value) {
throw new Error("Fail: clear")
} else {
console.log("OK: clear")
}
まとめ
- Vue.js の既存の API は
this
依存しているためテストが難しくなるなどの側面がある - composition-api は機能ごとの責任分割を容易にし、再利用性やテストの容易性を高めることに寄与する
プラクティス
- 前章のプラクティスで機能追加したTODOアプリケーションを composition-api を使って実装せよ
終わりに
非常に駆け足になったが、Webでのインタラクティブなアプリケーションを作成する方法、よりよいソフトウェアを書く方法、Vue.js を利用した宣言的なプログラミングをする方法、そして、よりモジュール性を高めるための composition-api について説明をした。
技術は利用するだけでも一定の効果を見込めることもあるが、重要なのはその技術がどのような特徴を持つか、なぜそのような特徴が有効なのかを理解して用いることと考えている。
本文書が、効果的に技術を利用することに寄与できれば嬉しい。
また、冒頭にも記載したが Vue.js の特徴であるカスタムコンポーネントについて記述できなかった。特に Vue.js で特徴てきな SFC に触れようと思うとどうしても webpack などのツールチェーンに触れる必要があるため断念した。SFC 触れずにコンポーネントシステムについての記事をいずれ作成したいと考えている。