15
10

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 3 years have passed since last update.

React Iframeで埋め込んだWebアプリに情報を渡す

Posted at

前回、Reactで作ったWebアプリ(以下、React アプリ)に、Vueで作ったWebアプリ(以下、Vue アプリ)をReact Iframeを用いて合体させました。React アプリからVue アプリに値を渡す必要があったので、今回は値を渡す方法についてまとめました。

やったこと

URLパラメータpostMessageの2つの方法を試してみました。

今回は、ユーザー名のaaa.bbbをReact アプリからVue アプリに渡して、Vueのページで表示したいと思います。

スクリーンショット 2021-05-23 17.26.42.png

URLパラメータ

URLの末尾にデータを付与して送ります。

Reactプロジェクト

Iframeに指定するURLのパラメータとして、?userName=aaa.bbbを加えます。

import React from 'react';
import Iframe from 'react-iframe'


export default function Page1(){

    return (
        <div>
            <Iframe id = 'page1'
                    url = '<埋め込みたいサイトURL>?userName=aaa.bbb'
                    position='absolute'
                    width='80%'
                    height='90%'/>
        </div>
    );
}

Vueプロジェクト

Vue Routerのインストール

URLパラメータで送られた情報をVueで取得するには、Vue Routerを使います。Vue Routerの使い方はこちらを参考にさせていただきました。

VueプロジェクトにVue Routerをインストールします。

$ yarn add vue-router

router.jsの作成

ルーティングを管理するためのrouter.jsファイルを作成します。デフォルトはhashモードになっているのですが、hashモードだと値が取れなかったので、historyモードを指定しています。

router.js
import Vue from 'vue'
import Router from 'vue-router'
import App from './App';

Vue.use(Router);

export default new Router({
    mode: 'history',
    routes: [
        {
            path: '/',
            name: 'App',
            component: App
        }
    ]
});

main.jsに追記

main.jsにも前述で作成したrouter.jsをインポートし、 Vue インスタンス作成処理に、routerをインジェクトします。

main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

値を使う

URLパラメータの値はthis.$route.query.${キー}で取得することができます。

<template>
  <div id='app'>
    <h1>{{ userName }}</h1>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      userName: 'ユーザー名'
    }
  },

  mounted: function(){
    this.userName = this.$route.query.userName
  }
}
</script>

これで、URLパラメータで情報を渡すことができました!

postMessage

postMessageを用いてデータを送ります。

Reactプロジェクト

Iframeの属性にonLoadを追加します。そうすると画像を含む画面の読み込みが完了した時に、指定しているloaded関数が実行されます。

postMessageについてはこちらを参考にしました。今回第二引数には、送り先(埋め込みたいサイト)のURLを指定してましたが、送り先を限定したくない場合は*を指定することもできます。

import React from 'react';
import Iframe from 'react-iframe'

export default function Page1(){

    const loaded = () => {
        const ifrm = document.getElementById('page1').contentWindow;
        ifrm.postMessage({
            userName: 'aaa.bbb',
        }, '<埋め込みたいサイトURL>');
    }

    return (
        <div>
            <Iframe id = 'page1'
                    url = '<埋め込みたいサイトURL>'
                    position='absolute'
                    width='80%'
                    height='90%'
                    onLoad={loaded}/>
        </div>
    );
}

(余談)
React Iframeのページには、onLoadの記載がなく、ここに辿り着くまで結構時間がかかってしまいました。

Vueプロジェクト

メッセージ受信時に実行されるwindow.addEventListenerのタイプmessageを使用して、Reactから送られてくる値をキャッチします。
また、送った値とともに送信元のオリジンも取得できるので、送信元オリジンのチェックも行っています。

<template>
  <div id='app'>
    <h1>{{ userName }}</h1>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      userName: 'ユーザー名'
    }
  },

  mounted: function(){
    const self = this
    const accessURL = '<送信元URL>'
    window.addEventListener('message', function(event) {
      if(event.origin === accessURL){
        self.userName = event.data.userName
      }
    }, false);
  }
}
</script>

これで、postMessageで値を渡すことができました!

つまずいたいたところ

React アプリからVue アプリに値を渡すためのpostMessageを実行するタイミングにつまずいたので、失敗した実装方法も残しておきます。

つまずき① React Routerの遷移では反応してくれない問題

// 失敗例
window.onload = function() {
    const ifrm = document.getElementById('page1').contentWindow;
    ifrm.postMessage({
        userName: 'aaa.bbb',
    }, '<埋め込みたいサイトURL>');
};

postMessageについて参考にしたサイトに記載している値の受け取り方をそのまま適用してみました。リロードをすると意図した通りに動作しますが、React Routerの遷移では、window.onloadは動きません。。

つまずき② 埋め込んだサイトの読み込みが終わっていない問題

// 失敗例
useEffect(() => {
    const ifrm = document.getElementById('page1').contentWindow;
    ifrm.postMessage({
        userName: 'aaa.bbb',
    }, '<埋め込みたいサイトURL>');
}, []);

つまずき①の結果から、コンポーネントのマウント時に実行されるようにuseEffectを使用してみました。もちろんこれだと、React Routerの遷移でもリロードでも動きますが、おそらく動き出したタイミングでは埋め込んだサイトの読み込みが終わっておらず、Vue アプリ側で値を受け取ることができませんでした。
なので、例えばuseEffect内で1秒待機をしてからpostMessageの処理が動き出すように実装すると、問題なく動作はしますが、待機か・・という気持ちになります。

その他、addEventListenerpageshowpopstateを使ってみたりしましたが、ことごとく上手くいかなかったです。

おわりに

埋め込んだサイトに値を渡す方法としてURLパラメータpostMessageという2つの方法についてまとめました。

実際に渡したかった値がセキュリティの観点からURLパラメータを使用するのは相応しくない値だったので、postMessageを使用するようにしました。ただ、同時に実行すると、URLパラメータのほうがややはやくReact アプリからVue アプリに値を渡せることがわかったので(後述)、セキュリティ的に問題ない値であれば、URLパラメータを使用するのもいいのかなと思いました。

また、今回はReact Routerを使ってVueで作ったWebアプリに飛びたいという要望があったこともあり、postMessageは実行タイミングを使いこなすのに時間がかかってしまいました。。
みなさん、こんなときはonLoadを使いましょう!他に良い方法がありましたら、コメントください!

おまけ 実行速度について

同時にするとどっちがはやいか選手権もやってみました。

Reactプロジェクト

import React from 'react';
import Iframe from 'react-iframe'

export default function Page1(){

    const loaded = () => {
        const ifrm = document.getElementById('page1').contentWindow;
        ifrm.postMessage({
            userName: 'aaa.bbb',
        }, '<埋め込みたいサイトURL>?userName=aaa.bbb');
    }

    return (
        <div>
            <Iframe id = 'page1'
                    url = '<埋め込みたいサイトURL>?userName=aaa.bbb'
                    position='absolute'
                    width='80%'
                    height='90%'
                    onLoad={loaded}/>
        </div>
    );
}

Vue プロジェクト

// 省略

<script>
export default {
  name: 'App',
  data() {
    return {
      userNameUrl : '',
      userNamePost : ''
    }
  },

  watch: {
    userNameUrl: function () {
      console.log(this.getTime() + ' URLパラメータ')
    },

    userNamePost: function () {
      console.log(this.getTime() + ' postMessage')
    }
  },

  mounted: function(){
    const self = this
    const accessURL = '<送信元URL>'
    window.addEventListener('message', function(event) {
      if(event.origin === accessURL){
        self.userNamePost = event.data.userName
      }
    }, false);
    this.userNameUrl = this.$route.query.userName
  }
}

// 現在時刻の取得省略

</script>

実行結果

22:32:05:763 URLパラメータ
22:32:05:778 postMessage

URLパラメータの方が若干はやいことがわかりました。URLパラメータだと値を送るのに、埋め込んだサイトの読み込みを待たなくていいからですかね。

15
10
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
15
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?