LoginSignup
2
1

More than 5 years have passed since last update.

Polymerでローカルストレージによる複数タブの同期を行いたい場合に注意すること

Last updated at Posted at 2017-05-25

Polymer でローカルストレージを使う

Polymer でローカルストレージを利用したい場合、app-storageapp-localstorage-document タグを利用します。

:warning: iron-localstorageは現時点で deprecated になっています。

app-localstorage-document タグはローカルストレージに変更があったとき data-changed というイベントを発火します。
このイベントがきたら、 event.detail.value もしくは、タグの data 属性から値を取得することができます。

例えば以下のように記述します。

<dom-module id="demo">
  <template>
    <app-localstorage-document id="storage" key="hoge"></app-localstorage-document>
  </template>

  <script>
    Polymer({
      is: 'demo',
      listeners: {
        'data-changed': '_dataChanged',
      },
      setValue: function(value) {
        this.$.storage.set('data', value);
      },
      _dataChanged: function(event) {
        console.log(event.detail.value);
      },
    });
  </script>
</dom-module>

demo エレメントを使ってブラウザを複数のタブで開くと、setValue したタブの値が、他のタブではコンソールに出力されます。

現状

この実装を様々なブラウザ(最新版)で試してみたところ、下記のような状況でした。

Chrome Firefox Safari IE11 Edge
OK OK OK NG NG

app-localstorage-document タグの実装を確認してみると、以下のようになっています。

app-localstorage-document.html#L82
      attached: function() {
        this.listen(window, 'storage', '__onStorage');
        this.listen(
            window.top,
            'app-local-storage-changed',
            '__onAppLocalStorageChanged');
      },

window.onstorage イベントでローカルストレージの変更を受け取って、__onStorage を呼び出しています。

app-localstorage-document.html#L182
      __onStorage: function(event) {
        if (event.key !== this.key ||
            event.storageArea !== this.storage) {
          return;
        }
        this.syncToMemory(function() {
          this.set('data', this.__parseValueFromStorage());
        });
      },

__onStorage では、__parseValueFromStorage を呼び出しています。

app-localstorage-document.html#L204
      __parseValueFromStorage: function() {
        try {
          return JSON.parse(this.storage.getItem(this.key));
        } catch(e) {
          console.error('Failed to parse value from storage for', this.key);
        }
      },

__parseValueFromStorage では、 this.storage で実際のローカルストレージから値を取得しています。

MS系のブラウザでは、window.onstorage イベントのタイミングでは、ストレージの内容は書き換わっていないようです。
(実際に開発者ツールを使って確認すると、イベント受信時はローカルストレージの値は古いままだったのですが、イベント処理が終わったあとで確認すると、新しい値になっていました。)

対応

https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onstorage
のサイトを確認すると、イベントオブジェクトには newValueoldValue が設定されていることがわかりました。

実際にその値をチェックすると、IE11やEdgeでも変更された値が入っていることがわかりました。
なので、先ほどの例を簡単に書き換えると、以下のようになります。

<dom-module id="demo">
  <template>
    <app-localstorage-document id="storage" key="hoge"></app-localstorage-document>
  </template>

  <script>
    Polymer({
      is: 'demo',
      setValue: function(value) {
        this.$.storage.set('data', value);
      },
      _dataChanged: function(event) {
        console.log(event.newValue);
      },
      ready: function() {
        this.listen(window, 'storage', '_dataChanged');
      }
    });
  </script>
</dom-module>

これでIE11やEdgeでも値の同期を行うことができるようになります。
ただし、このコードには問題があって、ローカルストレージのキーのチェックなどはしていないので、前述の __onStorage のようにチェックは必要になると思います。

issue を書いた

上記内容を issue にして上げておいたので、修正されると良いなーと思っています。
(時間があったらPRしたいとも思っている)

2
1
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
2
1