[Nuxt.js, Vue.js] Vue コンポーネントで Window のリサイズ/スクロールを監視するとてもよい方法
last updated at 2019.12.04
Vue で window
の resize
, scroll
などを扱いたいときって非常に悩ましいですね。いったいどこにイベントハンドラを記述すればよいのかしら。
コンポーネントの mounted()
メソッドで window.addEventListener('resize', ...)
としている解説記事などが見受けられますが、わたくしの美意識的にこれはあんまりいい方法だとは思えません。都度いちいち書いていかなくちゃいけないですし。どうせならどこかで一元管理したいですね。
そこで Vue v2.6.0 から追加された Vue.observable
とインスタンスプロパティを組み合わせてみてはどうでしょうというご提案。
Vue.observable
に渡されたオブジェクトはリアクティブになって返ってきます。それを Vue.prototype
に追加してグローバルに使ってしまおうということです。
サンプル
// /plugins/window.js
import Vue from 'vue'
Vue.use({
install(Vue) {
const $window = Vue.observable({
width: 0,
height: 0,
pageYOffset: 0
})
// SSR 時にエラーが出るため process.browser で分岐
// Nuxt を使用しなければこの分岐は削除してください
if (process.browser) {
const onScroll = () => {
$window.pageYOffset = global.pageYOffset
}
const onResize = () => {
$window.width = document.documentElement.clientWidth
$window.height = global.innerHeight
}
global.addEventListener('scroll', onScroll)
global.addEventListener('resize', onResize)
// 一度だけスクロールハンドラとリサイズハンドラを直接呼んで初期値をセット
onScroll()
onResize()
}
Vue.prototype.$window = $window
}
})
ウィンドウサイズとスクロール量を保持したリアクティブな $window
オブジェクトを作成し、 Vue.prototype.$window
に代入しました。これでどの Vue component からも this.$window
で参照できますのでコンポーネントで watch
すれば OK だ!
// /pages/**.vue, /components/**.vue などどこでも
export default {
watch: {
'$window.width'() {
console.log(`width: ${this.$window.width}`)
},
'$window.height'() {
console.log(`height: ${this.$window.height}`)
},
'$window.pageYOffset'() {
console.log(`pageYOfset: ${this.$window.pageYOffset}`)
}
}
}
あとは nuxt.config.js
の plugins
に window.js
を追加するだけです。
export default {
// ...省略
/*
** Plugins to load before mounting the App
*/
plugins: [{ src: '~/plugins/window.js' }],
// ...省略
}
プロダクションで使用するならばリサイズハンドラとスクロールハンドラは throttle-debounce などで間引いたほうがよいですがだいたいこんな感じでいいと思います。