4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.js: 外部のJSでの値の変更を反映させる(単純なstoreパターン)

Last updated at Posted at 2020-01-30

.vueファイルの外にある.jsファイルにオブジェクトを置き、そのプロパティの値が変更されたらVueコンポーネントであれこれする方法を紹介します。SPAまたはTurbolinksでページ遷移するときは、データを保持したまま使い回すこともできます。

要するに、単純なstoreパターンです。

state.png

次のような場合に使います。

  • 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のイベント処理が重複する可能性があるのでよくないです、念のため。)

WindowSize.js
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に指定します。ブラウザーのリサイズやスクロールに合わせて数値の表示が変わります。

Home.vue
<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'のように「オブジェクト.プロパティ」の形の関数名が使えます。

Home.vue
<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で中継することもできます。

Home.vue
<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は次のようになります。

WindowSize.js
export function ElementAppeared(elem) {
  if(elem) {
    let rect = elem.getBoundingClientRect();
    return (rect.top < store.state.height);
  }
  return false;
}
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?