マイクロフロントエンドの長所と利点

公開: 2022-03-11

マイクロフロントエンドアーキテクチャは、フロントエンドアプリを個別の半独立した「マイクロアプリ」に分解して、大まかに連携させる設計アプローチです。 マイクロフロントエンドの概念は、マイクロサービスに漠然と着想を得ており、マイクロサービスにちなんで名付けられています。

マイクロフロントエンドパターンの利点は次のとおりです。

  1. マイクロフロントエンドアーキテクチャはより単純である可能性があるため、推論と管理が容易になります。
  2. 独立した開発チームは、フロントエンドアプリでより簡単に共同作業を行うことができます。
  3. 「新しい」アプリを並行して実行することで、「古い」アプリから移行する手段を提供できます。

マイクロフロントエンドは最近多くの注目を集めていますが、現時点では、単一の主要な実装や明確な「最良の」マイクロフロントエンドフレームワークはありません。 実際、目的や要件に応じてさまざまなアプローチがあります。 よく知られている実装のいくつかについては、参考文献を参照してください。

この記事では、マイクロフロントエンドの理論の多くをスキップします。 これは私たちがカバーしないものです:

  • アプリをマイクロアプリに「スライス」する
  • マイクロフロントエンドがCI/CDモデルにどのように適合するかなどの展開の問題
  • テスト
  • マイクロアプリをバックエンドのマイクロサービスと1対1で連携させる必要があるかどうか
  • マイクロフロントエンドのコンセプトに対する批判
  • マイクロフロントエンドと単純な古いコンポーネントアーキテクチャの違い

代わりに、具体的な実装に焦点を当てたマイクロフロントエンドチュートリアルを紹介し、マイクロフロントエンドアーキテクチャの重要な問題とその可能な解決策に焦点を当てます。

私たちの実装はYumchaと呼ばれます。 広東語で「飲茶」の文字通りの意味は「お茶を飲む」ですが、日常の意味は「点心に出かける」です。 ここでの考え方は、マクロアプリ内の個々のマイクロアプリ(これを構成済みのトップレベルアプリと呼びます)は、点心ランチで持ち出される一口サイズのさまざまなバスケットに類似しているということです。

上記のマイクロフロントエンドベースのアプリの例の概要図。

Yumchaを「マイクロフロントエンドフレームワーク」と呼ぶこともあります。 今日の世界では、「フレームワーク」という用語は通常、Angular、React、Vue.js、またはWebアプリの他の同様の上部構造を指すために使用されます。 その意味でのフレームワークについてはまったく話していません。 便宜上、Yumchaをフレームワークと呼びます。これは、実際には、マイクロフロントエンドベースのアプリを構築するための一連のツールといくつかの薄いレイヤーです。

マイクロフロントエンドチュートリアルの最初のステップ:構成されたアプリのマークアップ

マクロアプリとそれを構成するマイクロアプリをどのように定義するかを考えてみましょう。 マークアップは常にWebの中心にあります。 したがって、macroappは、このマークアップよりも複雑なものによって指定されます。

 <html> <head> <script src="/yumcha.js"></script> </head> <body> <h1>Hello, micro-frontend app.</h1> <!-- HERE ARE THE MICROAPPS! --> <yumcha-portal name="microapp1" src="https://microapp1.example.com"></yumcha-portal> <yumcha-portal name="microapp2" src="https://microapp2.example.com"></yumcha-portal> </body> </html>

マークアップを使用してマクロアプリを定義すると、マイクロアプリをレイアウトおよび管理するためのHTMLとCSSの機能に完全にアクセスできます。 たとえば、あるマイクロアプリを別のマイクロアプリの上または横に置いたり、ページの隅に置いたり、アコーディオンの1つのペインに置いたり、何かが起こるまで非表示のままにしたり、バックグラウンドに永続的に留まったりすることができます。 。

マイクロアプリに使用されるカスタム要素に<yumcha-portal>という名前を付けました。これは、「ポータル」がポータル提案で使用されるマイクロアプリの有望な用語であり、マイクロフロントエンドで使用する標準のHTML要素を定義する初期の試みであるためです。

<yumcha-portal>カスタム要素の実装

<yumcha-portal>をどのように実装する必要がありますか? もちろん、これはカスタム要素なので、Webコンポーネントとしても使用できます。 マイクロフロントエンドWebコンポーネントの作成とコンパイルについては、多数の強力な候補の中から選択できます。 ここでは、PolymerProjectの最新のイテレーションであるLitElementを使用します。 LitElementは、TypeScriptベースのシンタックスシュガーをサポートしており、カスタム要素のボイラープレートのほとんどを処理します。 <yumcha-portal>をページで使用できるようにするには、上記のように、関連するコードを<script>として含める必要があります。

しかし、 <yumcha-portal>は実際に何をしているのでしょうか。 最初の概算は、指定されたソースでiframeを作成することです。

 render() { return html`<iframe src=${this.src}></iframe>`; }

…ここで、 renderは、 htmlタグ付きテンプレートリテラルを使用する標準のLitElementレンダリングフックです。 この最小限の機能は、いくつかの些細なユースケースにはほぼ十分かもしれません。

マイクロアプリをiframeに埋め込む

iframeは、誰もが嫌うHTML要素ですが、実際には、非常に便利で堅実なサンドボックス動作を提供します。 ただし、 iframeを使用する際に注意すべき問題のリストはまだたくさんあり、アプリの動作と機能に影響を与える可能性があります。

  • まず、 iframeには、サイズとレイアウトの点でよく知られている癖があります。
  • もちろん、CSSは、良くも悪くも、 iframe完全に分離されます。
  • ブラウザの「戻る」ボタンは適切に機能しますが、 iframeの現在のナビゲーションステータスはページのURLに反映されないため、URLを切り取って貼り付けて、作成されたアプリの同じ状態にすることも、ディープリンクを作成することもできませんでした。彼らへ。
  • CORSの設定によっては、外部からのiframeとの通信postMessageプロトコルを使用する必要がある場合があります
  • iframeの境界を越えた認証のための準備が必要になります。
  • 一部のスクリーンリーダーはiframeの境界でつまずいたり、ユーザーにアナウンスできるタイトルをiframeに付ける必要がある場合があります。

これらの問題のいくつかは、 iframeを使用しないことで回避または軽減できます。これは、この記事の後半で説明する代替手段です。

プラス面として、 iframeには独自の独立したContent-Security-Policy (CSP)があります。 また、 iframeが指すマイクロアプリがService Workerを使用するか、サーバー側のレンダリングを実装する場合、すべてが期待どおりに機能します。 また、 iframeにさまざまなサンドボックスオプションを指定して、上部のフレームに移動できるなど、その機能を制限することもできます。

一部のブラウザは、iframeのloading=lazy属性を出荷しているか、出荷する予定です。これは、ユーザーが近くをスクロールするまで、折り畳み下のiframeの読み込みをiframeしますが、これでは、遅延読み込みをきめ細かく制御できません。欲しい。

iframeの本当の問題は、 iframeのコンテンツが取得するために複数のネットワークリクエストを必要とすることです。 トップレベルのindex.htmlが受信され、そのスクリプトが読み込まれ、そのHTMLが解析されます。ただし、ブラウザはiframeのHTMLに対して別のリクエストを開始し、それを受信するのを待って、スクリプトを解析して読み込み、レンダリングする必要があります。 iframeのコンテンツ。 多くの場合、 iframeのJavaScriptは、スピンアップして独自のAPI呼び出しを行い、それらのAPI呼び出しが返され、データが表示用に処理された後にのみ、意味のあるデータを表示する必要があります。

これにより、特に複数のマイクロアプリが関係している場合に、望ましくない遅延やレンダリングアーティファクトが発生する可能性があります。 iframeのアプリがSSRを実装している場合、それは役立ちますが、それでも追加の往復の必要性を回避することはできません。

したがって、ポータル実装を設計する際に直面する重要な課題の1つは、このラウンドトリップの問題にどのように対処するかです。 私たちの目標は、単一のネットワークリクエストで、各マイクロアプリが事前入力できるコンテンツを含め、すべてのマイクロアプリを含むページ全体を停止することです。 この問題の解決策は、Yumchaサーバーにあります。

Yumchaサーバー

ここで紹介するマイクロフロントエンドソリューションの重要な要素は、マイクロアプリの構成を処理するための専用サーバーをセットアップすることです。 このサーバーは、各マイクロアプリがホストされているサーバーにリクエストをプロキシします。 確かに、このサーバーをセットアップして管理するには、ある程度の努力が必要です。 一部のマイクロフロントエンドアプローチ(シングルスパなど)は、展開と構成の容易さという名目で、このような特別なサーバーセットアップの必要性をなくそうとします。

ただし、このリバースプロキシを設定するコストは、私たちが得るメリットによって十分に相殺されます。 実際、マイクロフロントエンドベースのアプリには、それなしでは実現できない重要な動作があります。 このようなリバースプロキシを設定するための多くの商用および無料の代替手段があります。

リバースプロキシは、 microapp要求を適切なサーバーにルーティングするだけでなく、macroapp要求をmacroappサーバーにルーティングします。 そのサーバーは、作成されたアプリのHTMLを特別な方法で処理します。 http://macroapp.example.comなどのURLでプロキシサーバーを介してブラウザからindex.htmlのリクエストを受信すると、 index.htmlを取得し、単純ですが重要な変換を行ってから戻ります。それ。

具体的には、HTMLは<yumcha-portal>タグに対して解析されます。これは、Node.jsエコシステムで利用可能な有能なHTMLパーサーの1つを使用して簡単に実行できます。 <yumcha-portal>src属性を使用して、microappを実行しているサーバーに接続し、そのindex.htmlを取得します(サーバー側でレンダリングされたコンテンツがある場合)。 結果は、ブラウザによって実行されないように、 <script>または<template>タグとしてHTML応答に挿入されます。

Yumchaのサーバーアーキテクチャの図。ブラウザはリバースプロキシと通信し、リバースプロキシはマクロアプリと各マイクロアプリと通信します。 macroappステップは、アプリのメインのindex.htmlファイルを変換して事前入力します。

この設定の利点には、何よりもまず、作成されたページのindex.htmlの最初のリクエストで、サーバーが個々のmicroappサーバーから個々のページを取得できることが含まれます。 any-そして、追加のサーバーラウンドトリップなしでiframeにデータを取り込むために使用できるコンテンツを含む単一の完全なページをブラウザーに配信します(十分に使用されていないsrcdoc属性を使用)。 プロキシサーバーはまた、マイクロアプリが提供されている場所の詳細が詮索好きな目から隠されていることを確認します。 最後に、アプリケーションリクエストはすべて同じオリジンに対して行われるため、CORSの問題が単純化されます。

クライアントに戻ると、 <yumcha-portal>タグがインスタンス化され、サーバーによって応答ドキュメントに配置されたコンテンツを検索し、適切なタイミングでiframeをレンダリングして、コンテンツをsrcdoc属性に割り当てます。 iframeを使用していない場合(以下を参照)、その<yumcha-portal>タグに対応するコンテンツは、カスタム要素のシャドウDOM(使用している場合)に挿入されるか、ドキュメントに直接インラインで挿入されます。

この時点で、すでに部分的に機能しているマイクロフロントエンドベースのアプリがあります。

これは、Yumchaサーバーの興味深い機能という点で氷山の一角にすぎません。 たとえば、microappサーバーからのHTTPエラー応答の処理方法や、応答が非常に遅いmicroappの処理方法を制御する機能を追加したい場合、1つのmicroappがない場合は、ページを提供するために永遠に待つ必要はありません。応答します! これらおよびその他のトピックは、別の投稿に残します。

Yumcha macroapp index.html変換ロジックは、サーバーレスラムダ関数方式で、またはExpressやKoaなどのサーバーフレームワークのミドルウェアとして簡単に実装できます。

スタブベースのMicroappコントロール

クライアント側に戻ると、効率、遅延読み込み、ジャンクのないレンダリングに重要なマイクロアプリの実装方法に別の側面があります。 各マイクロアプリのiframeタグは、別のネットワークリクエストを行うsrc属性を使用するか、サーバーによって入力されたコンテンツが入力されたsrcdoc属性を使用して生成できます。 ただし、どちらの場合も、ユーザーが問題のマイクロアプリにアクセスしたことがない場合でも、すべてのスクリプトとリンクタグの読み込み、ブートストラップ、初期API呼び出しと関連データ処理など、 iframe内のコードがすぐに開始されます。

この問題に対する私たちの解決策は、最初にページ上のマイクロアプリを小さな非アクティブ化されたスタブとして表し、その後アクティブ化することです。 アクティベーションは、使用されていないIntersectionObserver APIを使用して、表示されるマイクロアプリの領域によって、またはより一般的には外部から送信される事前通知によって駆動できます。 もちろん、microappをすぐにアクティブ化するように指定することもできます。

いずれの場合も、microappがアクティブ化されたときにのみ、 iframeが実際にレンダリングされ、そのコードが読み込まれて実行されます。 LitElementを使用した実装に関して、アクティブ化ステータスがactivatedれたインスタンス変数で表されると仮定すると、次のようになります。

 render() { if (!this.activated) return html`{this.placeholder}`; else return html` <iframe srcdoc="${this.content}" @load="${this.markLoaded}"></iframe>`; }

マイクロアプリ間通信

マクロアプリを構成するマイクロアプリは、定義上、緩く結合されていますが、それでも相互に通信できる必要があります。 たとえば、ナビゲーションマイクロアプリは、ユーザーが選択した他のマイクロアプリをアクティブ化する必要があるという通知を送信する必要があり、アクティブ化するアプリはそのような通知を受信する必要があります。

私たちのミニマリストの考え方に沿って、私たちは多くのメッセージパッシング機構を導入することを避けたいと思っています。 代わりに、Webコンポーネントの精神で、DOMイベントを使用します。 差し迫ったイベントのすべてのスタブに事前通知し、そのイベントタイプがアクティブ化されるまでアクティブ化を要求したものを待機し、マイクロアプリがリッスンできるドキュメントに対してイベントをディスパッチする簡単なブロードキャストAPIを提供しますそれ。 すべてのiframeが同一生成元であるとすると、 iframeからページに、またはその逆にアクセスして、イベントを発生させる要素を見つけることができます。

ルーティング

今日、私たちは皆、SPAのURLバーがアプリケーションの表示状態を表すことを期待するようになりました。そのため、切り取り、貼り付け、メール、テキスト、およびそれにリンクして、アプリ内のページに直接ジャンプできます。 ただし、マイクロフロントエンドアプリでは、アプリケーションの状態は実際には状態の組み合わせであり、各マイクロアプリに1つずつあります。 これをどのように表現し、制御するのでしょうか。

解決策は、各microappの状態を単一の複合URLにエンコードし、その複合URLをまとめて分離する方法を知っている小さなmacroappルーターを使用することです。 残念ながら、これには各microappにYumcha固有のロジックが必要です。macroappルーターからメッセージを受信して​​microappの状態を更新し、逆に、macroappルーターにその状態の変化を通知して複合URLを更新できるようにします。 たとえば、Angularの場合はYumchaLocationStrategy 、Reactの場合は<YumchaRouter>要素を想像できます。

マクロアプリの状態を表す複合URL。そのクエリ文字列は、2つの別々の(二重にエンコードされた)クエリ文字列にデコードされ、IDがキーとして指定されているマイクロアプリに渡されます。

iframeケース

上記のように、 iframeでマイクロアプリをホストすることにはいくつかの欠点があります。 2つの選択肢があります。ページのHTMLに直接インラインで含めるか、ShadowDOMに配置します。 どちらの方法も、 iframeの長所と短所をある程度反映していますが、場合によっては異なる方法で反映されます。

たとえば、個々のmicroappCSPポリシーを何らかの方法でマージする必要があります。 スクリーンリーダーなどの支援技術は、シャドウDOMをサポートしていると仮定すると(まだすべてではない)、 iframeよりもうまく機能するはずです。 「スコープ」のサービスワーカーの概念を使用してマイクロアプリのサービスワーカーを登録するように手配するのは簡単ですが、アプリはサービスワーカーが"/"ではなくアプリの名前で登録されていることを確認する必要があります。 iframeに関連するレイアウトの問題は、インラインまたはシャドウDOMメソッドには適用されません。

ただし、AngularやReactなどのフレームワークを使用して構築されたアプリケーションは、インラインまたはシャドウDOMでの生活に不満を抱く可能性があります。 それらについては、 iframeを使用したいと思うでしょう。

CSSに関しては、インラインDOMメソッドとシャドウDOMメソッドが異なります。 CSSはシャドウDOMにきれいにカプセル化されます。 何らかの理由でCSSの外部をシャドウDOMと共有したい場合は、構築可能なスタイルシートなどを使用する必要があります。 インライン化されたマイクロアプリを使用すると、すべてのCSSがページ全体で共有されます。


結局、 <yumcha-portal>にインラインおよびシャドウDOMマイクロアプリのロジックを実装するのは簡単です。 特定のマイクロアプリのコンテンツを、サーバーロジックによってHTML <template>要素としてページに挿入された場所から取得し、クローンを作成してから、LitElementがrenderRootと呼ぶものに追加します。これは通常は要素のシャドウDOMですが、インライン(非シャドウDOM)の場合は、要素自体( this )にも設定されます。

ちょっと待って! microappサーバーによって提供されるコンテンツは、HTMLページ全体です。 htmlheadbodyタグを含むmicroappのHTMLページを、macroappのページの中央に挿入することはできませんか?

この問題は、microappサーバーから取得したmicroappコンテンツがラップされているtemplateタグの癖を利用して解決します。 最近のブラウザがtemplateタグに遭遇すると、「実行」しませんが、解析し、 <html><head><body>タグなどの無効なコンテンツを削除します。 、内部コンテンツを保持しながら。 したがって、 <head><script>タグと<link>タグ、および<body>のコンテンツは保持されます。 これはまさに、マイクロアプリのコンテンツをページに挿入するために必要なものです。

マイクロフロントエンドアーキテクチャ:悪魔は詳細に宿る

マイクロフロントエンドは、(a)より優れたアーキテクチャアプローチであることが判明し、(b)今日のWebの無数の実用的な要件を満たす方法でそれらを実装する方法を理解できれば、webappエコシステムに定着します。

最初の質問に関しては、マイクロフロントエンドがすべてのユースケースに適したアーキテクチャであるとは誰も主張していません。 特に、単一のチームによるグリーンフィールド開発がマイクロフロントエンドを採用する理由はほとんどありません。 マイクロフロントエンドパターンから他のコメンテーターに最も利益をもたらす可能性があるのは、どのタイプのコンテキストのどのタイプのアプリかという質問を残しておきます。

実装と実現可能性の観点から、この記事で言及されていないもの、特に認証とセキュリティ、コードの複製、SEOなど、関係するさまざまな詳細があります。 それでも、この記事で、マイクロフロントエンドの基本的な実装アプローチを説明し、さらに改良を加えることで、実際の要件に対応できることを願っています。

参考文献

  • マイクロフロントエンド—角度のあるスタイルで行う—パート1
  • マイクロフロントエンド—角度のあるスタイルで行う—パート2
  • マイクロフロントエンドを使用してAngularJSアプリケーションを進化させる
  • マイクロフロントエンド
  • UIマイクロサービス—アンチパターンを逆転させる(マイクロフロントエンド)
  • UIマイクロサービス—アンチパターン?
  • マイクロフロントエンドを使用したページ構築では、リバースプロキシとSSIのYumchaのようなアプローチを採用しています。これを強くお勧めします。
  • マイクロフロントエンドリソース
  • 表彰台
  • 私はマイクロフロントエンドを理解していません。 これは、マイクロフロントエンドアーキテクチャのタイプとユースケースのかなり良い概要です。
  • Vue.js、AWS Lambda、Hypernovaを使用したサーバーレスマイクロフロントエンド
  • マイクロフロントエンド:優れた包括的な概要。