blog

iframeをベースに、フロントエンドとフロントエンドの連携も非常にスムーズだ!

ページがイベントに耳を傾け、その後、親ページがハッシュを変更し、子ページは、通信を達成するためにハッシュを読み取ります。しかし、これには問題があります。あまりにも多くの情報が渡されると、非常に長いUR...

Mar 14, 2020 · 7 min. read
シェア

通常、需要を行うには、フロントエンドとバックエンドの調整であり、時には複数のクライアントの調整があります。しかし、まだいくつかのニーズがあり、フロントエンドとフロントエンドのチューニングの必要性 - インラインiframeは、いくつかの非常に複雑なページが直接インラインに選択することがありますが、iframeを達成するための方法のいずれかの非常にホットなマイクロフロントエンドが存在する、最終的なページは基本的に2つのフロントエンドページの通信以下ではありません。フロントエンドとフロントエンドのチューニング時間は、バックエンドのチューニング時間と比較して、より多くを行う必要があります。フロントエンドとフロントエンドのチューニングは、データレベルではありませんので、また、ページの状態情報の転送があります。以下は、フロントエンドのカップリング通信方式のセットを探索するために

技術的な選択

  1. hashchange

ページはhashchangeイベントをリッスンし、親ページはハッシュを変更し、子ページはハッシュを読み込んで通信します。しかし、これには問題があります。渡される情報が多すぎると、URLが長くなってしまいますし、メンテナンスも面倒です。さらに深刻な問題は、ページ自体にハッシュを利用するロジックがある場合、解決策がないということです!

  1. storage

解決できますが、ストレージデータの冗長性につながり、また余分なデータを適時に削除する必要があります。一般的には使用されず、複数タブの通信に適しています。

  1. postmessage

これが最も安定した解決策になるはずで、新たな副作用をもたらすこともなく、データ量を心配する必要もありません。フォレンジック・チェックのロジックを追加すれば、より完全なものになります。

デザインのアイデア

ポストメッセージオプションを選択した場合、考慮すべきことがあります:

  1. 認証が必要です。そうしないとセキュリティ上の問題があります。
  2. を使用する場合は、要求のようなhttpは同じ未分化の経験を要求し、フロントエンドの通信への唯一の根本的な変更
  3. 約束コールバックスタイル
  4. パラメータとデータの前処理と後処理をサポート
  5. 拡張が簡単

実装の詳細

送信&収集

現在サブページにいて、リクエストをしていると仮定します:

window.parent && window.parent.postMessage({
 api: 'getUserInfo', payload: { id: 1 } 
}, '*');

回収依頼の処理

window.IFRAME_APIS = {
	getUserInfo({ id }) {
 	// idでユーザー情報を引き出し、返す
 // どのようにそれを返すには、サブページで、その後handleGetUserInfoSuccメソッドを定義する
 iframeElement.postMessage({
 api: 'handleGetUserInfoSucc', payload: { name: 'lhyt', age: 23 } 
 })
 }
}
window.addEventListener('message', ({ data }) => {
 try {
 console.log('recive data', data);
 window.IFRAME_APIS[data.api](data.payload);
 } catch (e) {
 console.error(e);
 }
});

子ページが親ページにリクエストしてデータを取得し、親ページが子ページのメソッドを呼び出して成功を処理します。handleGetUserInfoSuccもちろん、子ページのaddEventListenerもまったく同じコードで、IFRAME_APISの中にあらかじめメソッドを用意しておく必要があります。

認証...

addEventListenerは何らかの認証が必要で、そうでなければセキュリティ・リスクになります。最もシンプルで効果的な方法は、アクセスリストチェックを追加することです。

const FR_ALLOW_LIST = ['sourceA', 'sourceB']
window.addEventListener('message', ({ data }) => {
 if (!data || typeof data !== 'object') {
 return;
 }
 if (FR_ALLOW_LIST.includes(data.fr)) {
 try {
 console.log('recive data', data);
 window.IFRAME_APIS[data.api](data.payload);
 } catch (e) {
 console.error(e);
 }
 } else {
 throw Error('unknown fr!')
 }
});

後で、他のフロントエンドといくつかのソース値frについて合意し、これらのapiにアクセスできるかどうかをチェックすることができます。

プロミスをサポートする方法

ご覧のように、子ページがリクエストを送信すると、親ページは成功したリクエストを返し、子ページは事前に別のメソッドを用意しなければならず、非常に面倒です。明らかにプロミスが必要な場合は、通常通りrequest/axios/fetchを使用して処理します。解決すべき問題

  • postMessageは、構造化クローンアルゴリズムで直列化できるデータのみを渡すことができます。
  • promiseのresolve関数とreject関数は直接渡すことができないので、別の方法で間接的に呼び出す必要があります。
//  
// ストア解決、拒否する
const resolvers = {};
const rejecters = {};
window.IFRAME_APIS = {
// 約束を処理する関数を準備する
 resolvePromise({ payload, resolve }) {
 if (resolvers[resolve]) {
 resolvers[resolve](payload || {});
 }
 delete resolvers[resolve];
 delete rejecters[resolve];
 },
 }
// 子ページは、親ページを要求する
function requestParent({ api, payload }) {
 return new Promise((resolve, reject) => {
 const rand = Math.random().toString(36).slice(2);
 window.parent.postMessage({
 api, payload: {
 ...payload,
 resolve: rand,
 reject: rand,
 } 
 }, '*');
 resolvers[rand] = resolve;
 rejecters[rand] = reject;
 })
}

親ページは、子ページに resolve を実行するように指示する関数を実装しなければなりません。

function sendResponse(payload) {
 iframe.contentWindow.postMessage(
 {
 payload: { resolve: payload.resolve, payload },
 fr: 'sourceA',
 api: 'resolvePromise',
 },
 '*'
 );
}

このプロセスは、子ページが親ページにリクエストを送信するときに、キーも一緒に渡し、キーとresolve/rejectのマッピングを維持するというものです。親ページは子ページのresolvePromiseを呼び出し、間接的にresolve/rejectを実行します。このようにして、例えばのように、プロミス型によって呼び出されるすべてのリクエストをこの方法で実行することができます。

//  
requestParent({ api: 'a', payload: { fr: 'sourceA', a: 1, b: '2' } })
.then(console.log)
//  
window.IFRAME_APIS = {
// 約束関数sendResponseに対処する準備ができて内部
 a(payload) {
 sendResponse({ resolve: payload.resolve, msg: 'succ' })
 },
 }

前処理&

要求された場所ごとに特別な処理をする必要がないように、上流で統一された処理ロジックを追加する必要があることもあります。後処理についても、フォーマットのグローバルな適応を行います。

const prefix = {
	a(params) {
 	params.b = 2;
 	return params
 },
 b(params) {
 // loadingリクエストしない場合
 	if (params.loading) {
 	return false
 }
 	return params
 }
}
const afterfix = {
	a(data) {
 	return {
 	...data,
 msg: 'afterfix success'
 }
 }
}
function requestParent({ api, payload }) {
 //  
	if (prefix[api]) {
 	payload = prefix[api](payload)
 }
 //  
 if (!payload) {
 	return Promise.resolve({})
 }
 return new Promise((resolve, reject) => {
 const rand = Math.random().toString(36).slice(2);
 window.parent.postMessage({
 api, payload: {
 ...payload,
 resolve: rand,
 reject: rand,
 } 
 }, '*');
 resolvers[rand] = data => {
 // ここで後処理をする
 	if (afterfix[api]) {
 	data = afterfix[api](data)
 }
 	return resolve(data)
 };
 rejecters[rand] = reject;
 })
}

昇格させる必要がないものもありますが、一方通行の呼び出しであり、promiseによって呼び出されない関数を追加で書いたり、パラメータを追加して制御することができます。また、Promise.raceにタイムアウト処理を追加したり、通常のリクエストに変更したり、タイマーを追加したりできるpromise呼び出しメソッドもあります。

拡張可能

すべてのリクエストを事前にIFRAME_APISの中に入れる必要があるわけではなく、コンポーネントの中に書くべき組み込みコンポーネントの依存関係があるものもありますし、リクエストを削除する必要がない場合もあります。そのため、iframe-apiを拡張する関数と、それを削除する関数、補助的なデータのメンテナンスが必要です。

const ext = {}
function injectIframeApi(api, fn, injectExt) {
 function remove() {
 delete window.IFRAME_APIS[api];
 }
 // これは、補助データを拡張することである、EMは、時には確かにいくつかの追加の補助データの必要性である
 injectExt(ext);
 // 見ての通り、nullをfnに渡すのはext.Nullを更新するだけだ。
 if (fn === null) {
 return remove;
 }
 if (window.IFRAME_APIS[api]) {
 return remove;
 }
 window.IFRAME_APIS[api] = fn;
 return remove;
}

リクエスト時に使用する ext メカニズムを追加します。

function requestParent({ api, payload }) {
 //  
	if (prefix[api]) {
-- payload = prefix[api](payload)
++ 	payload = prefix[api](payload, ext)
 }
 //  
 if (!payload) {
 	return Promise.resolve({})
 }
 return new Promise((resolve, reject) => {
 const rand = Math.random().toString(36).slice(2);
 window.parent.postMessage({
 api, payload: {
 ...payload,
 resolve: rand,
 reject: rand,
 } 
 }, '*');
 resolvers[rand] = data => {
 // ここで後処理をする
 	if (afterfix[api]) {
-- 	data = afterfix[api](data)
++ 	data = afterfix[api](data, ext)
 }
 	return resolve(data)
 };
 rejecters[rand] = reject;
 })
}
window.addEventListener('message', ({ data }) => {
 try {
 console.log('recive data', data);
-- window.IFRAME_APIS[data.api](data.payload);
++ window.IFRAME_APIS[data.api](data.payload, ext);
 } catch (e) {
 console.error(e);
 }
});

コンポーネント内部などで使用する場合

window.IFRAME_APIS = {
	a(params, ext) {
 	if (ext.loading) {
 	return false
 }
 retuan params
 }
}
function C({ loading }) {
	useEffect(() => {
 	// を要求するときは、ロードの値を見る必要がある
 	injectIframeApi('a', null, ext => {
 	ext.loading = loading
 })
 }, [loading])
 
 // コンポーネント固有の要求関数は、あなたがそれを必要としないときは、それを行うことはできない!
 useEffect(() => {
 	const remove = injectIframeApi('someapi', data => {
 	console.log(data, 'this is iframe api data')
 })
 return remove
 }, [])
	return <section />
}

最後に

このように、それは通常の要求とまったく同じ方法で使用することができ、また、さまざまな処理や拡張機能をサポートし、それはhttpリクエストを開始するのとまったく同じ方法で無差別の経験です。もちろん、自分の状況に応じて、裁量変更より快適なああ、例えば、一部の人々は、ノードのエラーがコールバックスタイルの最初のパラメータを置くのが好き、一部の人々は、axiosのスタイルが好き、一部の人々は、オブジェクト指向のスタイルが好き、これらは裁量変更にこのアイデアの周りにすることができます、同様に自分のために最も適した!

Read next

Spring Cloud Streamメッセージドライバコンポーネント

本体MQメッセージングミドルウェアの詳細の違いは、特定のデータベースをブロックアウトHibernateのようなものです。これにより、MQの学習、開発、保守が簡単になります。Spring Cloud Streamは現在、RabbitMQとKafkaをサポートしています。 Spring Cloud Streamは...

Mar 14, 2020 · 4 min read