前書き
前回は「Vue インスタンス」まで読み進めました。
いよいよ今回は Vue.js の基本になるであろうテンプレート構文を読んでいきます。
途中、「ドキュメントのサンプルコードだけじゃ分からん……」となった箇所で、自分なりに理解するためにコードを書いています。いうまでもなくガチ初心者が書いたコードなので、エレガントじゃないとか色々あると思いますが、そこは目を瞑ってください。
ただ、間違った解釈をしていたり、不足していることがあったときは、優しくマサカリをコメント欄に置いていっていただけますと幸いです。
テンプレート構文
ドキュメントの順序通り書き下したいところですが、「ディレクティブって何ぞや?」と乱舞する片仮名が気になって仕方ないので、簡単な説明の部分だけ先にさらってしまいましょう。
ディレクティブ(雑な要約)
ディレクティブとは、v- から始まる特別な属性です。ディレクティブの仕事は、属性値の式が変化したときに、リアクティブに副作用を DOM に適用することです。
「はじめに」の条件分岐とループのチュートリアルが分かりやすい例ですね。v-if ディレクティブが、seen という式の真偽値をもとに <p> 要素を削除したり挿入していました。
ディレクティブについては後でまた書き下します。
用語が分かったところで、改めてドキュメントの上から順に読んでいきましょう。
展開
テキスト
もっとも基本的な形は Mustache構文 という、{{}} を使ったテキスト展開です。宣言的レンダリングの最初のチュートリアルに出てきましたね。
チュートリアルで体験した通り、対応するオブジェクトのプロパティの値に置き換わります。プロパティの値を変更すると、それに応じて更新されます。
v-once を使うことで、値を変更しても更新を行わず、一度だけ展開することができます。
ただし、v-once が使われていない部分では影響を受けます。
ドキュメントのコードだとピンと来なかったので、実験的にコードを書いてみました。
<div id="app_A" v-once>
{{ message }}
</div>
<div id="app_B">
{{ message }}
</div>
{{message}} を突っ込んだ2つの div があります。div#app_A には v-once を指定し、div#app_B には何も指定していません。
両方に message の内容が表示されるようにコードを書きます。
var msg = {
message: 'Hello Vue!'
}
var app_A = new Vue({
el: '#app_A',
data: msg
})
var app_B = new Vue({
el: '#app_B',
data: msg
})
Hello Vue!
Hello Vue!
無事に同じテキストが表示されました!
では、コンソールで app_A.message の内容を test に変えてみましょう。
Hello Vue!
test
div#app_A には v-once が指定されているので、message の内容が変更されても「Hello Vue!」が表示されたままです。一方、div#app_B は message の更新に応じて表示が変わりました。
念のためコンソールで app_A.message の中身を見てましょう。 test になっていますね。
ドキュメントで述べられている「同じノードのあらゆる他のバインディングが影響を受ける」というのは、こういうことだと思います。多分。
生のHTML
{{}} に入れたデータは、HTMLではなくプレーンなテキストとして扱われます。
ドキュメントではHTMLコードと結果しか書いておらず、肝心の rawHtml に何が入っているのか一見わかりづらいですが、<span style="color:red">This should be red.</span> という文字列が入っています。
前述の通り、{{}} で展開されたデータはプレーンなテキストとして扱われるので、文字列がそのまま出ています。
v-html ディレクティブを使用したほうは、プレーンなHTMLテキストとして解釈されるので、「This should be red.」という赤字が表示されます。
Vue は、文字列ベースのテンプレートエンジンではないので~の部分はよくわかりませんでした。取り敢えず今は、みだりに使わないようにするという程度の理解で進めます。ちゃんと説明できるようになったら加筆します。
属性
{{}} は、HTML属性の内部で使えません。
<!-- dynamicId にIDとして指定したい文字列を入れた想定 -->
<div id="{{dynamicId}}"></div>
<div id={{dynamicId}}></div>
<!-- dynamicId に id="hoge" みたいな文字列を入れた想定 -->
<div {{dynamicId}}></div>
代わりに、v-bind を使います。ドキュメントに書いてありますが、比較のため再掲してみましょう。
<div v-bind:id="dynamicId"></div>
v-bind: と書いた後に、使いたいHTML属性を書いて、データを指定する!って感じで書けば良さそうですね。
HTML属性が存在していることを true や false といった真偽値属性で示す場合、v-bind の挙動は少しだけ変わります。
ドキュメントのコードを元に実験してみましょう。まずは true の場合です。
<button id="app" v-bind:disabled="isButtonDisabled">Button</button>
var app = new Vue({
el: '#app',
data: {
isButtonDisabled: true
}
})
<button id="app" disabled="disabled">Button</button>
次に、isButtonDisabled の値を false にしてみます。
var app = new Vue({
el: '#app',
data: {
isButtonDisabled: false
}
})
<button id="app">Button</button>
disabled 属性がまるごと出力されませんでしたね。
このように、真偽値属性が false、null、undefined の場合は、HTML属性を含まず要素が描画されます。
JavaScript 式の使用
Vue.js は、データバインディング(=データと対象を結び付けて反映する)内部で、JavaScript の式を使うことができます。
ドキュメントのコードを使って、確かめていきます。
<div id="app">
<p>{{ number + 1 }}</p>
<p>{{ ok ? 'YES' : 'NO' }}</p>
<p>{{ message.split('').reverse().join('') }}</p>
<div v-bind:id="'list-' + id">sample</div>
</div>
var app = new Vue({
el: '#app',
data: {
number: 1,
ok: false,
message: 'ABC',
id: 'a'
}
})
<p>2</p>
<p>NO</p>
<p>CBA</p>
<div id="list-a">sample</div>
HTML 側で書いた JavaScript の式が、結果に反映されました。
注意したいのは、どんな式でも動作するわけではないことです。
ドキュメントで挙げられているような「変数の宣言」「if文」といった単一の式ではないもの(要はその一文だけでは完結しない式、という認識でいいのかな……)は対象外です。
ディレクティブ
先に書いた通り、v- から始まる特別な属性を指します。v-for を除くディレクティブ属性値は、単一の JavaScript 式を期待し、その式が変化したとき、リアクティブに副作用を DOM に適用します。
引数
ディレクティブ名の後にコロンを表記して、引数をとるディレクティブもあります。
例えば、 v-bind は、HTML 属性を引数として、リアクティブにその値を更新します。v-on の場合は、click などの受け取りたいイベント名を引数として取ります。
v-ディレクティブ名:引数="値"
動的引数
Vue.js バージョン2.6.0から、[] で囲むことで、JavaScript 式をディレクティブの引数に使うことができるようになりました。
v-ディレクティブ名:[値1]="値2"
ただし、いくつかの制約があります。
-
値 の制約
要はnullと string(文字列)しか使えない、ということだと思います。真偽値とか入ってても、ねえ……ってことでしょう。多分。
nullは明示的にバインディングを削除するのにつかわれる、とドキュメントにありますが、例となるコードが無いのでちょっと理解ができませんでした。 -
式 の制約
構文上の制約があります。例えばスペースとか引用符なんかは、HTML 属性としては不正な文字なので、使ってはいけません。それらを含まない式を使うか、置き換えが必要です。
また、HTML ファイルに直接書く場合、ブラウザが属性名を強制的に小文字にしてしまいます。小文字変換後のプロパティがあれば動きますが、後々管理に苦労することが容易に想像つくので、キャメルケースなど大文字を混ぜるようなことは原則として避けましょう(というか、禁止という認識で良い問題ないと思います)。
<!-- スペースや引用符が含まれている -->
<a v-bind:['foo' + bar]="value"> ... </a>
<!-- この someAttr は someattr に変換されるので、someattr プロパティが無いと動かない -->
<a v-bind:[someAttr]="value"> ... </a>
修飾子
修飾子(Modifier といいます)は、. で表記された特別な接尾語です。ディレクティブ v- を、特別な方法でバインドすることを示します。
サンプルコードがよくわからないので、v-on や v-model の説明を読むときに改めて復習しようと思います。
省略記法
v- 接頭辞のうち、最もよく使われるディレクティブである v-bind や v-on には特別な省略記法が提供されています。
<!-- 完全な構文 -->
<a v-bind:href="url"> ... </a>
<!-- 省略記法 -->
<a :href="url"> ... </a>
<!-- 動的引数の省略記法 (2.6.0 以降) -->
<a :[key]="url"> ... </a>
<!-- 完全な構文 -->
<a v-on:click="doSomething"> ... </a>
<!-- 省略記法 -->
<a @click="doSomething"> ... </a>
<!-- 動的引数の省略記法 (2.6.0 以降) -->
<a @[event]="doSomething"> ... </a>
修飾子がつくときはどうするんだろう?と思ったのですが、ちょうど先程の v-on の修飾子のページを見ていた時に見つけました。普通に後ろにくっつけて良さそうです。
<button @click.ctrl="onClick">A</button>
次回予告
基本的な記法が分かって楽しくなってきた半面、ドキュメントの咀嚼が難しくなってきました……。
順番通り、次は算出プロパティを頑張って読んでいこうと思います。