はじめに
データバインディングを利用することでDOMの更新は効率化されるが、直接DOMを参照したい場合もある。例えば、画面上の要素の位置や高さはDOMを直接参照しなければ分からない。
DOMに直接アクセスするにはインスタンスプロパティの$el
と$refs
を使用する。
この2つはDOMを参照するため、ライフサイクルのmounted以降でなければ使用できない
。
$el(ルートを参照)
$elを使用することでインスタンスやコンポーネントのルートを参照することができる。
<div id="app">
<p>Hello</p>
</div>
new Vew({
el: 'app',
mounted: function() {
// <div id="app"></div>が出力される
console.log(this.$el)
}
})
ルートとは
HTMLはツリー構造になっているが、その最上位に位置するのがルートである。HTMLドキュメントではhtml
要素がルートにあたる。
$refs(DOMの要素を参照)
ルート以外のDOMの要素を参照する場合は、ref
属性と$refs
を使用する。
ref
はDOM要素を参照するための特別な属性で$refs
のキーとなる。
<div id="app">
<!-- 対象となる要素にref属性で名前をつける -->
<p ref="hello">Hello</p>
</div>
new Vue({
el: 'app',
mounted: function() {
// <p>Hello</p>が出力される
console.log(this.$refs.hello);
}
})
動的にDOMを参照
動的にする場合、refはv-bindディレクティブを使って:ref
と記述する。
v-for
で要素を繰り返し表示している場合はv-for部分は配列構造になっているのでscript内でアクセスするにはthis.$refs[ref属性でつけた名前]
と記述する。
<div v-for="item in items" :key="item.id">
<!-- 動的にするためrefにv-bindをつける -->
<p :ref="name">{{ item.name }}</p>
</div>
new Vue ({
el: 'app',
data: {
items: [
{
id: '001',
name: 'apple'
},
{
id: '002',
name: 'orange'
}
]
},
mounted: function() {
// <p>apple</p>が出力される
console.log(this.$refs[name][0]);
}
})
$elと$refsは一時的な変更
$elと$refsは一時的な変更なので、データが更新されたり、アプリケーションが操作されて再描画されると上書きされる(変更がリセットされる)。仮想DOMではなくリアルDOMを参照しているからである。
リアルDOMと仮想DOM
VueにはリアルDOM
と仮想DOM
という2つのDOMが存在する。
リアルDOM
リアルDOMは実際にブラウザを描画しているものである。
Vueを使用しないでリアルDOMのみで描画している場合、変更を加えた際はHTML内の全てを読み込むので描画に時間がかかる。
<リアルDOMのみの描画の流れ>
- HTML
- リアルDOM
- WEBページ
仮想DOM
仮想DOMは擬似的に作られたDOM。仮想DOMの中には変更前と変更後の2つのDOMが用意されていて、この2つのDOMの差分のみがリアルDOMに反映されるようになっている。
差分のみを読み込み反映するので、描画のスピードが速い。
<仮想DOMを用いた描画の流れ>
- HTML
- 仮想DOM →変更前のDOM & 変更後のDOM(この2つの差分のみリアルDOMへ反映)
- リアルDOM
- WEBページ
上記の説明を踏まえて動作を確認してみる
<div id="app">
<!-- ボタンをクリックするとhandleClickメソッドが実行される -->
<button @click="handleClick">カウントアップ</button>
<!-- ボタンをクリックするとshowの値が反転する -->
<button @click="show = !show">v-show 表示/非表示</button>
<button @click="show = !show">v-if 表示/非表示</button>
<!-- 値の真偽値によって要素を表示/非表示(非表示の場合display:none;の状態) -->
<p ref="count" v-show="show">0<p>
<!-- 値の真偽値によって要素を表示/非表示(非表示の場合DOMから要素が削除されている状態) -->
<p ref="count" v-if="show">0<p>
</div>
new Vue({
el: 'app',
data: {
show: true
},
methods: {
handleClick: function() {
// ref属性で紐づけたcountを変数countに代入
var count = this.$refs.count;
// count(pタグ)の中のテキストを整数(10進数)にして1プラスする
if(count) {
count.innerText = parseInt(count.innerText, 10) + 1
}
}
}
})
上記のコードではカウントアップボタンをクリックするとpタグの中の数値が1ずつアップしていく仕組みになっている。
v-if
で表示を切り替えている部分はDOMから要素自体が削除されている(仮想DOMを切り替えている)ので、非表示にして表示し直すとpタグの数値は0
にリセットされる。
それに対しv-show
で切り替えている部分はdisplay:none;
で見えなくなっているだけで要素自体は存在している(リアルDOMには何の影響も与えていない)ので、非表示にして表示し直すとカウントアップした数値はそのまま維持される。
これらのことから$refs
と$el
の変更は一時的だということがわかる。