.vueファイルの外にある.jsファイルにオブジェクトを置き、そのプロパティの値が変更されたらVueコンポーネントであれこれする方法を紹介します。SPAまたはTurbolinksでページ遷移するときは、データを保持したまま使い回すこともできます。
要するに、単純なstoreパターンです。
次のような場合に使います。
- windowオブジェクトのイベント処理(resizeやscroll)
- RailsのActionCableでのデータ受信をイベントのように扱う
- ユーザーの選択結果を一時的に(ページがリロードされない限り)保存しておく
- Ajaxで取得したデータがもったいないので使い回す
storeデータの表示
2020-02-01修正: storeオブジェクトの中にstateオブジェクトを持たせる形に変更。
ブラウザーのサイズ変更やスクロールを検出するサンプルです。
WindowSize.jsにオブジェクトstoreを置いてエクスポートします。windowのresizeとscrollイベントで、store.stateのプロパティの値を変更します。
(Vueのcreatedやmountedで window.addEventListener
を行うのは、windowのイベント処理が重複する可能性があるのでよくないです、念のため。)
const store = {
state: {
width: window.innerWidth,
height: window.innerHeight,
scrollX: window.pageXOffset,
scrollY: window.pageYOffset
}
};
window.addEventListener('resize', () => {
store.state.width = window.innerWidth;
store.state.height = window.innerHeight;
});
window.addEventListener('scroll', () => {
store.state.scrollX = window.pageXOffset;
store.state.scrollY = window.pageYOffset;
});
export default store;
.vueファイルでは、WindowSize.jsのオブジェクトをインポートして、dataのプロパティwinSizeに指定します。ブラウザーのリサイズやスクロールに合わせて数値の表示が変わります。
<template>
<div>
<div>{{winSize.width}}x{{winSize.height}}</div>
<div>{{winSize.scrollX}},{{winSize.scrollY}}</div>
<div style="width: 2000px; height: 1000px; background-color: #eee">
</div>
</div>
</template>
<script>
import WindowSize from '../states/WindowSize';
export default {
data() {
return {
winSize: WindowSize.state
};
}
}
</script>
storeデータの変更をwatchする
外部オブジェクトのプロパティに値が代入されたときに、イベント処理のように何か行うにはwatchを使います。次の例は、ページを下までスクロールして、ref="message"
のdiv要素が見えたら表示を変えるものです。
watchの中では、'winSize.scrollY'
のように「オブジェクト.プロパティ」の形の関数名が使えます。
<template>
<div>
<div>{{winSize.width}}x{{winSize.height}}</div>
<div>{{winSize.scrollX}},{{winSize.scrollY}}</div>
<div style="width: 2000px; height: 1000px; background-color: #eee">
</div>
<div ref="message">{{message}}</div>
</div>
</template>
<script>
import WindowSize, { ElementAppeared } from '../states/WindowSize';
export default {
data() {
return {
winSize: WindowSize.state,
message: 'Waiting...'
};
},
watch: {
'winSize.scrollY'() {
if(ElementAppeared(this.$refs.message)) {
this.message = 'Hello!';
}
}
}
}
</script>
次のようにcomputedで中継することもできます。
<script>
import WindowSize, { ElementAppeared } from '../states/WindowSize';
export default {
data() {
return {
winSize: WindowSize.state,
message: 'Waiting...'
};
},
computed: {
scrollY() {
return this.winSize.scrollY;
}
},
watch: {
scrollY() {
if(ElementAppeared(this.$refs.message)) {
this.message = 'Hello!';
}
}
}
}
</script>
ここで使っている関数ElementAppearedは次のようになります。
export function ElementAppeared(elem) {
if(elem) {
let rect = elem.getBoundingClientRect();
return (rect.top < store.state.height);
}
return false;
}