たまたまVue.jsのv-bind
について強く調べる機会があって、実はv-bind
の持つ全ての機能について説明してる記事って無いかも?と思ったので、「ぜんぶ」とまではいかないかもしれないけど思いつく範囲のv-bind
の機能を列挙するつもりです。
Vue.jsのバージョンは3に限定します(v2も調べ直すの面倒なので)。
v-bind
の基本
Vue.jsのディレクティブの一つです。つまりテンプレートに記述するHTML属性っぽいところに記述できるVue.jsの構文の一つです。
ディレクティブがなんなのかのドキュメントはここ(英語)です。
v-bind
のAPIとしてのドキュメントはここ(英語)です。
基本機能
HTML要素に使用すると、属性を動的に設定できます。コンポーネントに使用すると、コンポーネントのプロパティを動的に設定できます。
HTML要素に使用する例:
<template>
<input v-bind:type="myInputType">
</template>
この場合、出力されるHTMLは<input>
のtype
属性に、myInputType
に保存された値が指定されます。
myInputType
='password'
の場合に出力されるHTMLの具体例は次です。
<input type="password">
コンポーネントに使用する例:
<template>
<MyComponent v-bind:message="myMessage" />
</template>
このコードは、<MyComponent>
コンポーネントのmessage
プロパティに、myMessage
に保存された値が指定されます。HTMLの結果はコンポーネントの定義によるので割愛します。
基本構文
HTMLではv-bind:【属性名】="【JavaScript式】"
のように記述します。記述は同じですがコンポーネントに使用するとv-bind:【プロパティ名】="【JavaScript式】"
となります。
Vue.jsのディレクティブとして説明すると、ディレクティブ名がbind
で、引数には属性名かプロパティ名を指定します。スクリプトにその属性かプロパティに与える値を取り出す式を記述するということになります。
(ディレクティブはv-【ディレクティブ名】:【引数】.【修飾子】="スクリプト"
みたいな構造を持ちます。詳細はこちら(英語))
JavaScript式が書けるということは、次の例に書くv-bind
は、いずれも有効なv-bind
です。
<template>
<input v-bind:type="myInputType">
<input v-bind:type="myTypes.foo.bar">
<input v-bind:type="types[kind]">
<input v-bind:type="getType(foo)">
<MyComponent v-bind:my-str-prop="prefix + str" />
<MyComponent v-bind:my-num-prop="num * 42" />
<MyComponent v-bind:my-obj-prop="{ foo: 1, bar: 2, baz }" />
<MyComponent v-bind:my-arr-prop="[1, 2, 3, foo]" />
</template>
またv-bind
には省略記法があり、
:【属性名】="【JavaScript式】"
のように記述しても同じです。
<template>
<input :type="myInputType">
<input :type="myTypes.foo.bar">
<input :type="types[kind]">
<input :type="getType(foo)">
<MyComponent :my-str-prop="prefix + str" />
<MyComponent :my-num-prop="num * 42" />
<MyComponent :my-obj-prop="{ foo: 1, bar: 2, baz }" />
<MyComponent :my-arr-prop="[1, 2, 3, foo]" />
</template>
特別な使用
v-bind:class
ドキュメントはこの辺り(英語)です。
v-bind:class
のオブジェクト形式
普通のclass
属性として考えると文字列のみ利用できそうですが、v-bind:class
にはオブジェクトを与えることができます。
与えるオブジェクトは、{ '【class名】': 【真偽値】 }
のような形式のオブジェクトです。真偽値がtrue
の場合は、そのclass
名が適用されfalse
の場合は適用されません。条件によってclass
を制御する時に便利です(文字列結合と三項演算子など使用する必要はありません)。
<template>
<div v-bind:class="{ 'item--active': isActive, 'item--hidden': !isShown }"></div>
</template>
isActive
とisShown
がどちらもtrue
の時はitem--active
の値のみがtrue
のため、item--active
だけがclass
属性に付与されます。つまり次のHTMLが出力されます。
<div class="item--active"></div>
v-bind:class
の配列形式
v-bind:class
には配列を与えることができます。配列の要素には与えるclass
名、およびオブジェクトを列挙します。すると配列で与えられたclass
名が全て適用されます。
例:
<template>
<div v-bind:class="['foo','bar']"></div>
<div v-bind:class="['foo','bar', { 'item--active': isActive, 'item--hidden': !isShown }]"></div>
</template>
HTMLの出力:
<div class="foo bar"></div>
<div class="foo bar item--active"></div>
通常のclass
属性と併用
通常の属性は、同じ属性に対して、v-bind
と、普通の属性としての記述は併用できませんが、class
属性と次に紹介するstyle
属性は、併用することができます。
v-bind:class="..."
で与えるclass
名と、class="..."
で与えるclass
名はマージして適用されます。
例:
<template>
<div
class="foo bar"
v-bind:class="['baz','qux']">
</div>
</template>
HTMLの出力:
<div class="foo bar baz qux"></div>
v-bind:style
ドキュメントはこの辺り(英語)です。
v-bind:style
のオブジェクト形式
v-bind:style
もオブジェクトを与えることができます。
与えるオブジェクトは、{ '【style名】': '【styleの値】' }
のような形式のオブジェクトです。文字列結合を使用して、style
を頑張って作り出す必要はありません。また、プロパティ名に指定する【style名】はキャメルケースでも有効です。
<template>
<div v-bind:style="{ color: myColor, fontSize: myFontSize }"></div>
</template>
myColor
が"red"
でmyFontSize
が"14px"
の場合、次のHTMLが出力されます。
<div style="color: red; font-size: 14px;"></div>
v-bind:style
の配列形式
v-bind:style
には配列を与えることができます。配列の要素には与えるオブジェクトを列挙します。すると配列で与えられたstyle
が全て適用されます。
例:
<template>
<div v-bind:style="[{ color: myColor }, { fontSize: myFontSize }]"></div>
</template>
HTMLの出力:
<div style="color: red; font-size: 14px;"></div>
自動プレフィックス
v-bind:style
とオブジェクトを使用して与えたスタイル名は、そのブラウザで動作するために必要なベンダープレフィックスを自動的に付与します。
例:
<template>
<div v-bind:style="{ 'line-clamp': 3 }"></div>
</template>
HTMLの出力(Chromeの場合):
<div style="-webkit-line-clamp: 3;"></div>
通常のstyle
属性と併用
v-bind:style
とstyle
属性は、併用することができます。
v-bind:style="..."
で与えるstyle
と、style="..."
で与えるstyle
はマージして適用されます。
例:
<template>
<div
style="color: red;"
v-bind:style="{ fontSize: '14px' }">
</div>
</template>
HTMLの出力:
<div style="color: red; font-size: 14px;"></div>
v-bind:key
ドキュメントはこの辺り(英語)です。
(本当はkey
属性(特別な属性)はv-bind
とは関係ないですが、併用することが多いと思うので書いておきます。)
key
属性を与えると、Vueは実際のDOMを描画するためのヒントとして使用します。よく使用される例をいくつか書いておきます。
ちなみにkey
属性は実際のHTMLには出力されません。
v-for
と使用
v-for
一緒に使用する場合は、最適化が主な目的です。
ドキュメントはこの辺り(英語)です。
<template>
<li v-for="item in items"
v-bind:key="item.id">
{{ item.text }}
</li>
</template>
v-for
が<template>
にある場合は、v-bind:key
は<template>
に記述します。
<template>
<ul>
<template v-for="item in items"
v-bind:key="item.id">
<li>{{ item.text1 }}</li>
<li>{{ item.text2 }}</li>
</template>
</ul>
</template>
<transition-group>
と使用
<transition-group>
でアニメーションを意図通りに動かすためには、<transition-group>
の子要素でkey
を使用します。
ドキュメントはこの辺り(英語)です。
<template>
<transition-group name="list">
<span v-for="item in items" v-bind:key="item.id">
{{ item.text }}
</span>
</transition-group>
</template>
より高度な構文
先に書いた、基本構文以外の構文的な機能を書きます。
動的属性名(プロパティ名)
v-bind
はディレクティブなので動的引数を使用できます。
つまり、v-bind:【名前】
で指定していた【名前】は動的に変更できます。
<template>
<input v-bind:[foo]="bar">
</template>
これはfoo
が"data-hello-bind"
で、bar
が"BAR DATA"
だった場合に出力されるHTMLの具体例は次です。
<input data-hello-bind="BAR DATA">
もちろん省略記法でも使用できます。
<template>
<input :[foo]="bar">
</template>
引数無し
v-bind="object"
と指定するとオブジェクトのプロパティとして与えた値を一括で適用します。
つまり、
<template>
<input v-bind="{ type:'text', maxlength: 3 }">
</template>
は、HTMLでは、
<input type="text" maxlength="3">
となります。
.camel
修飾子
v-bind:【名前】
で指定した【名前】を、キャメルケースに変換して実際に与えるらしいです(自分は使ったことないです。SVGで現れるキャメルケースの属性名の時に有用という噂を聞いたことがありますがよく知りません)。
詳細はこちら(英語)を参照してください。
次のような形で使用します。
<svg v-bind:view-box.camel="viewBox /* viewBox='0,0,0,0'*/"></svg>
出力はこのような形。
<svg viewBox="0,0,0,0"></svg>
より高度な使用
<slot>
に使用
スコープ付きスロットはv-bind
を使用します。
ドキュメントはこの辺り(英語)です。
<slot>
でv-bind
を使用すると、子コンポーネントからv-bind
で与えられた値を、スロットコンテンツを生成する親コンポーネント側で使用できます。これはスコープ付きスロットと言われます。
例:
<template>
<div class="child">
<slot v-bind:foo="myFoo" />
</div>
</template>
<script>
export default {
data() {
return { myFoo: "Hello" }
}
}
</script>
<template>
<div class="parent">
<ChildComponent>
<template v-slot="{ foo }">
{{ foo }} world
</template>
</ChildComponent>
</div>
</template>
HTMLの出力:
<div class="parent">
<div class="child">
Hello world
</div>
</div>
詳細はドキュメントを参照してください。
v-bind:ref
ドキュメントはこの辺り(英語)です。
普通にref="foo"
と書くと、これを書いた要素(またはコンポーネントのインスタンス)が$refs.foo
経由でアクセスできるようになりますが、v-bind:ref
を使用して、コールバック関数を与えるとコールバック関数によって、これを書いた要素(またはコンポーネントのインスタンス)を処理できます。
<template>
<div v-bind:ref="(el) => child = el /* childプロパティで受け取ります */">Foo</div>
</template>
<script>
export default {
data() {
return { child: null }
},
mounted() {
console.log(this.child) /* <div>Foo</div> が出力されます。 */
}
}
</script>
v-bind:is
ドキュメントはこの辺り(英語)です。
is
は<component>
というVueの組み込みコンポーネントのプロパティです。
詳細はドキュメントを参照してください。
HTML属性と値
いくつかの値は、それを与えるHTML属性によって、振る舞いが違います。
ドキュメントとしてはこの辺り(英語)とRFCだとこれ(英語)です。
null
または undefined
null
かundefined
を与えると属性自体が付与されません。属性ごと与えたくない(属性を削除)したい場合は、null
かundefined
を与えておけば良さそうです。
空白文字列(''
)では属性自体はついたままなので要注意ということでもあります。
<template>
<input v-bind:type="nullValue">
<input v-bind:type="undefinedValue">
<input v-bind:type="falseValue">
<input v-bind:type="emptyValue">
<input v-bind:type="zeroValue">
</template>
<script>
export default {
data () {
return {
nullValue: null,
undefinedValue: undefined,
falseValue: false,
emptyValue: '',
zeroValue: 0
}
},
}
</script>
<input>
<input>
<input type="false">
<input type>
<input type="0">
false
真偽値属性(例えば<input>
のdisabled
)の場合は空白文字列(''
)以外のfalse
な値を与えると属性自体が付与されません。
こちらも空白文字列(''
)では属性自体はついたままなので要注意です。特に属性としてはtrue
扱いであることに注意してください。
<template>
<input v-bind:disabled="nullValue">
<input v-bind:disabled="undefinedValue">
<input v-bind:disabled="falseValue">
<input v-bind:disabled="emptyValue">
<input v-bind:disabled="zeroValue">
</template>
<script>
export default {
data () {
return {
nullValue: null,
undefinedValue: undefined,
falseValue: false,
emptyValue: '',
zeroValue: 0
}
},
}
</script>
<input>
<input>
<input>
<input disabled>
<input>
まとめ
v-bind
の説明というより、Vueが特別に扱う属性とプロパティの機能説明って感じになってしまった気もしますね(汗)
再度書きますが、Vue 3限定なので、Vue 2では使用できないことも書いてあります。
他のディレクティブも書いてみたい気持ちもあったけど、v-bind
だけで結構疲れたのでもういいかな。
しかし、本当にこれで全部なのかどうかは自信ないです。。