Kekuatan dan Manfaat Mikro Frontend

Diterbitkan: 2022-03-11

Arsitektur micro-frontend adalah pendekatan desain di mana aplikasi front-end didekomposisi menjadi “microapps” semi-independen yang bekerja secara longgar bersama-sama. Konsep mikro-frontend secara samar-samar terinspirasi oleh, dan dinamai, layanan mikro.

Manfaat dari pola mikro-frontend meliputi:

  1. Arsitektur mikro-frontend mungkin lebih sederhana, dan dengan demikian lebih mudah untuk dipikirkan dan dikelola.
  2. Tim pengembangan independen dapat berkolaborasi di aplikasi front-end dengan lebih mudah.
  3. Mereka dapat menyediakan sarana untuk bermigrasi dari aplikasi "lama" dengan menjalankan aplikasi "baru" secara berdampingan.

Meskipun frontend mikro telah mendapatkan banyak perhatian akhir-akhir ini, hingga saat ini belum ada implementasi dominan tunggal dan tidak ada kerangka kerja mikro-frontend "terbaik" yang jelas. Sebenarnya, ada berbagai pendekatan tergantung pada tujuan dan persyaratan. Lihat bibliografi untuk beberapa implementasi yang lebih dikenal.

Pada artikel ini, kita akan melewatkan banyak teori frontend mikro. Inilah yang tidak akan kami bahas:

  • "Mengiris" aplikasi ke dalam aplikasi mikro
  • Masalah penerapan, termasuk bagaimana antarmuka mikro cocok dengan model CI/CD
  • Pengujian
  • Apakah aplikasi mikro harus selaras satu-ke-satu dengan layanan mikro di backend
  • Kritik terhadap konsep mikro-frontend
  • Perbedaan antara mikro frontend dan arsitektur komponen lama biasa

Sebagai gantinya, kami akan menyajikan tutorial mikro-frontend yang berfokus pada implementasi konkret, menyoroti isu-isu penting dalam arsitektur mikro-frontend dan kemungkinan solusinya.

Implementasi kami disebut Yumcha. Arti harfiah dari “yum cha” dalam bahasa Kanton adalah “minum teh”, tetapi arti sehari-harinya adalah “keluar untuk membeli dim sum.” Idenya di sini adalah bahwa masing-masing aplikasi mikro dalam aplikasi makro (seperti yang akan kita sebut aplikasi tingkat atas yang tersusun) dianalogikan dengan berbagai keranjang porsi ukuran gigitan yang dibawakan saat makan siang dim sum.

Ikhtisar ilustrasi contoh aplikasi berbasis mikro frontend seperti yang dijelaskan di atas.

Kami terkadang menyebut Yumcha sebagai "kerangka kerja mikro-frontend." Di dunia sekarang ini, istilah "kerangka kerja" biasanya digunakan untuk merujuk ke Angular, React, Vue.js, atau suprastruktur serupa lainnya untuk aplikasi web. Kami sama sekali tidak berbicara tentang kerangka kerja dalam pengertian itu. Kami menyebut Yumcha kerangka hanya demi kenyamanan: Ini sebenarnya lebih dari satu set alat dan beberapa lapisan tipis untuk membangun aplikasi berbasis mikro frontend.

Tutorial Mikro-frontend Langkah Pertama: Markup untuk Aplikasi yang Disusun

Mari selami dengan memikirkan tentang bagaimana kita dapat mendefinisikan aplikasi makro dan aplikasi mikro yang menyusunnya. Markup selalu menjadi inti dari web. Oleh karena itu, aplikasi makro kami akan ditentukan oleh tidak ada yang lebih rumit dari markup ini:

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

Mendefinisikan macroapp kami menggunakan markup memberi kami akses penuh ke kekuatan HTML dan CSS untuk menata dan mengelola microapps kami. Misalnya, satu aplikasi mikro dapat duduk di atas yang lain, atau di sampingnya, atau berada di pojok halaman, atau berada di salah satu panel akordeon, atau tetap tersembunyi hingga sesuatu terjadi, atau tetap berada di latar belakang secara permanen .

Kami telah menamai elemen kustom yang digunakan untuk microapps <yumcha-portal> karena "portal" adalah istilah yang menjanjikan untuk microapps yang digunakan dalam proposal portal, upaya awal untuk mendefinisikan elemen HTML standar untuk digunakan di frontend mikro.

Menerapkan Elemen Kustom <yumcha-portal>

Bagaimana seharusnya kita mengimplementasikan <yumcha-portal> ? Karena ini adalah elemen khusus, sebagai komponen web, tentu saja! Kami dapat memilih di antara sejumlah pesaing kuat untuk menulis dan menyusun komponen web mikro-frontend; di sini kita akan menggunakan LitElement, iterasi terbaru dari Proyek Polimer. LitElement mendukung gula sintaksis berbasis TypeScript, yang menangani sebagian besar boilerplate elemen kustom untuk kami. Untuk membuat <yumcha-portal> tersedia untuk halaman kita, kita harus memasukkan kode yang relevan sebagai <script> , seperti yang kita lakukan di atas.

Tapi apa yang sebenarnya <yumcha-portal> lakukan? Perkiraan pertama adalah dengan membuat iframe dengan sumber yang ditentukan:

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

…di mana render adalah kait rendering LitElement standar, menggunakan literal templat yang diberi tag html . Fungsionalitas minimal ini mungkin hampir cukup untuk beberapa kasus penggunaan sepele.

Menanamkan Microapps di iframe s

iframe s adalah elemen HTML yang disukai semua orang, tetapi sebenarnya mereka menyediakan perilaku sandboxing yang sangat berguna dan kokoh. Namun, masih ada daftar panjang masalah yang harus diperhatikan saat menggunakan iframe s, dengan potensi dampak pada perilaku dan fungsionalitas aplikasi kami:

  • Pertama, iframe s memiliki kebiasaan yang terkenal dalam hal ukuran dan tata letaknya.
  • CSS tentu saja akan sepenuhnya diisolasi ke iframe , baik atau buruk.
  • Tombol "kembali" browser akan bekerja dengan cukup baik, meskipun status navigasi iframe saat ini tidak akan tercermin dalam URL halaman , jadi kami tidak dapat memotong dan menempelkan URL untuk mendapatkan status aplikasi yang sama, atau tautan dalam ke mereka.
  • Komunikasi dengan iframe dari luar, bergantung pada penyiapan CORS kami, mungkin perlu melalui protokol postMessage .
  • Pengaturan harus dibuat untuk otentikasi melintasi batas iframe .
  • Beberapa pembaca layar mungkin tersandung pada batas iframe atau memerlukan iframe untuk memiliki judul yang dapat mereka umumkan kepada pengguna.

Beberapa masalah ini dapat dihindari atau dikurangi dengan tidak menggunakan iframe s, alternatif yang akan kita bahas nanti di artikel.

Di sisi positifnya, iframe akan memiliki Content-Security-Policy (CSP) sendiri yang independen. Juga, jika aplikasi mikro yang ditunjuk iframe menggunakan pekerja layanan atau mengimplementasikan rendering sisi server, semuanya akan berfungsi seperti yang diharapkan. Kami juga dapat menentukan berbagai opsi sandboxing ke iframe untuk membatasi kemampuannya, seperti dapat menavigasi ke bingkai atas.

Beberapa browser telah mengirimkan atau berencana mengirimkan atribut loading=lazy untuk iframe s, yang menunda pemuatan iframe s paruh bawah hingga pengguna menggulir di dekat mereka, tetapi ini tidak memberikan kontrol yang baik untuk pemuatan lambat itu kami ingin.

Masalah sebenarnya dengan iframe s adalah bahwa konten iframe akan mengambil beberapa permintaan jaringan untuk diambil. index.html tingkat atas diterima, skripnya dimuat, dan HTML-nya diuraikan—tetapi kemudian browser harus memulai permintaan lain untuk HTML iframe , menunggu untuk menerimanya, mengurai dan memuat skripnya, dan merender isi iframe . Dalam banyak kasus, JavaScript iframe masih harus berputar, membuat panggilan API sendiri, dan menampilkan data yang berarti hanya setelah panggilan API tersebut kembali dan data diproses untuk dilihat.

Ini kemungkinan akan mengakibatkan penundaan yang tidak diinginkan dan rendering artefak, terutama ketika beberapa aplikasi mikro terlibat. Jika aplikasi iframe mengimplementasikan SSR, itu akan membantu tetapi tetap tidak menghindari kebutuhan untuk perjalanan pulang pergi tambahan.

Jadi salah satu tantangan utama yang kami hadapi dalam merancang implementasi portal kami adalah bagaimana menangani masalah bolak-balik ini. Tujuan kami adalah bahwa satu permintaan jaringan harus menurunkan seluruh halaman dengan semua aplikasi mikronya, termasuk konten apa pun yang dapat diisi sebelumnya oleh masing-masing aplikasi. Solusi untuk masalah ini terletak pada server Yumcha.

Server Yumcha

Elemen kunci dari solusi mikro-frontend yang disajikan di sini adalah menyiapkan server khusus untuk menangani komposisi aplikasi mikro. Server ini mem-proxy permintaan ke server tempat setiap microapp di-host. Memang, perlu beberapa upaya untuk menyiapkan dan mengelola server ini. Beberapa pendekatan mikro-frontend (misalnya, spa tunggal) mencoba untuk menghilangkan kebutuhan akan pengaturan server khusus seperti itu, atas nama kemudahan penerapan dan konfigurasi.

Namun, biaya pengaturan proxy terbalik ini lebih dari diimbangi oleh manfaat yang kami peroleh; sebenarnya, ada perilaku penting dari aplikasi berbasis mikro frontend yang tidak dapat kami capai tanpanya. Ada banyak alternatif komersial dan gratis untuk menyiapkan proxy terbalik seperti itu.

Proxy terbalik, selain merutekan permintaan microapp ke server yang sesuai, juga merutekan permintaan macroapp ke server macroapp. Server itu menyajikan HTML untuk aplikasi yang dibuat dengan cara khusus. Setelah menerima permintaan untuk index.html dari browser melalui server proxy di URL seperti http://macroapp.example.com , ia mengambil index.html dan kemudian mengubahnya menjadi transformasi sederhana namun penting sebelum kembali dia.

Secara khusus, HTML diuraikan untuk <yumcha-portal> , yang dapat dilakukan dengan mudah dengan salah satu pengurai HTML kompeten yang tersedia di ekosistem Node.js. Menggunakan atribut src ke <yumcha-portal> , server yang menjalankan microapp dihubungi dan index.html -nya diambil—termasuk konten yang dirender sisi server, jika ada. Hasilnya dimasukkan ke dalam respons HTML sebagai <script> atau <template> , agar tidak dieksekusi oleh browser.

Ilustrasi arsitektur server Yumcha. Peramban berkomunikasi dengan proxy terbalik, yang pada gilirannya berkomunikasi dengan aplikasi makro dan masing-masing aplikasi mikro. Langkah macroapp mengubah dan mengisi file index.html utama aplikasi.

Keuntungan dari pengaturan ini termasuk, pertama dan terutama, bahwa pada permintaan pertama untuk index.html untuk halaman yang dibuat, server dapat mengambil halaman individual dari server microapp individu secara keseluruhan—termasuk konten yang diberikan SSR, jika any—dan mengirimkan satu halaman lengkap ke browser, termasuk konten yang dapat digunakan untuk mengisi iframe tanpa server tambahan pulang pergi (menggunakan atribut srcdoc yang kurang digunakan). Server proxy juga memastikan bahwa setiap detail dari mana microapps dilayani terselubung dari pengintaian. Akhirnya, ini menyederhanakan masalah CORS, karena semua permintaan aplikasi dibuat ke asal yang sama.

Kembali ke klien, <yumcha-portal> akan dipakai dan menemukan konten di mana ia ditempatkan di dokumen respons oleh server, dan pada waktu yang tepat membuat iframe dan menetapkan konten ke atribut srcdoc -nya. Jika kita tidak menggunakan iframe s (lihat di bawah), maka konten yang sesuai dengan <yumcha-portal> itu akan dimasukkan ke dalam shadow DOM elemen kustom, jika kita menggunakannya, atau langsung inline dalam dokumen.

Pada titik ini, kami sudah memiliki aplikasi berbasis mikro frontend yang berfungsi sebagian.

Ini hanyalah puncak gunung es dalam hal fungsionalitas yang menarik untuk server Yumcha. Misalnya, kami ingin menambahkan fitur untuk mengontrol bagaimana respons kesalahan HTTP dari server microapp ditangani, atau bagaimana menangani microapp yang merespons dengan sangat lambat—kami tidak ingin menunggu selamanya untuk menayangkan halaman jika satu microapp tidak menanggapi! Ini dan topik lainnya kita akan tinggalkan untuk posting lain.

Logika transformasi Yumcha macroapp index.html dapat dengan mudah diimplementasikan dalam mode fungsi lambda tanpa server, atau sebagai middleware untuk kerangka kerja server seperti Express atau Koa.

Kontrol Microapp berbasis rintisan

Kembali ke sisi klien, ada aspek lain tentang cara kami mengimplementasikan aplikasi mikro yang penting untuk efisiensi, pemuatan lambat, dan rendering bebas jank. Kita dapat membuat tag iframe untuk setiap aplikasi mikro, baik dengan atribut src —yang membuat permintaan jaringan lain—atau dengan atribut srcdoc yang diisi dengan konten yang diisi untuk kita oleh server. Namun dalam kedua kasus tersebut, kode di iframe itu akan segera dimulai, termasuk memuat semua skrip dan tag tautannya, bootstrap, dan panggilan API awal serta pemrosesan data terkait—bahkan jika pengguna bahkan tidak pernah mengakses aplikasi mikro yang dimaksud.

Solusi kami untuk masalah ini adalah awalnya menampilkan aplikasi mikro pada halaman sebagai rintisan kecil yang tidak aktif, yang kemudian dapat diaktifkan. Aktivasi dapat didorong baik oleh wilayah aplikasi mikro yang terlihat, menggunakan API IntersectionObserver yang kurang digunakan, atau lebih umum dengan pra-pemberitahuan yang dikirim dari luar. Tentu saja, kami juga dapat menentukan bahwa aplikasi mikro segera diaktifkan.

Bagaimanapun, ketika dan hanya ketika microapp diaktifkan, iframe benar-benar dirender dan kodenya dimuat dan dieksekusi. Dalam hal implementasi kami menggunakan LitElement, dan dengan asumsi status aktivasi diwakili oleh variabel instan yang activated , kami akan memiliki sesuatu seperti:

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

Komunikasi antar aplikasi mikro

Meskipun aplikasi mikro yang membentuk aplikasi makro menurut definisi digabungkan secara longgar, mereka masih harus dapat berkomunikasi satu sama lain. Misalnya, aplikasi mikro navigasi perlu mengirimkan pemberitahuan bahwa beberapa aplikasi mikro lain yang baru saja dipilih oleh pengguna harus diaktifkan, dan aplikasi yang akan diaktifkan perlu menerima pemberitahuan tersebut.

Sejalan dengan pola pikir minimalis kami, kami ingin menghindari memperkenalkan banyak mesin pengirim pesan. Sebagai gantinya, dalam semangat komponen web, kami akan menggunakan acara DOM. Kami menyediakan API siaran sepele yang memberi tahu semua rintisan tentang peristiwa yang akan datang, menunggu apa pun yang telah meminta untuk diaktifkan agar jenis peristiwa itu diaktifkan, dan kemudian mengirimkan peristiwa tersebut ke dokumen, di mana aplikasi mikro mana pun dapat mendengarkan dia. Mengingat bahwa semua iframe s kita adalah asal yang sama, kita dapat menjangkau dari iframe ke halaman dan sebaliknya untuk menemukan elemen yang akan digunakan untuk mengaktifkan peristiwa.

Rute

Di zaman sekarang ini, kita semua mengharapkan bilah URL di SPA untuk mewakili status tampilan aplikasi, sehingga kita dapat memotong, menempel, mengirim email, teks, dan menautkannya untuk melompat langsung ke halaman di dalam aplikasi. Namun, dalam aplikasi mikro-frontend, status aplikasi sebenarnya adalah kombinasi status, satu untuk setiap aplikasi mikro. Bagaimana kita mewakili dan mengendalikan ini?

Solusinya adalah dengan mengkodekan setiap status microapp ke dalam satu URL komposit dan menggunakan router macroapp kecil yang tahu bagaimana menyatukan URL komposit itu dan memisahkannya. Sayangnya, ini membutuhkan logika khusus Yumcha di setiap microapp: untuk menerima pesan dari router macroapp dan memperbarui status microapp, dan sebaliknya untuk memberi tahu router macroapp tentang perubahan status itu sehingga URL komposit dapat diperbarui. Misalnya, orang dapat membayangkan YumchaLocationStrategy untuk Angular, atau elemen <YumchaRouter> untuk React.

URL gabungan yang mewakili status aplikasi makro. String kuerinya diterjemahkan menjadi dua string kueri terpisah (dikodekan ganda) yang kemudian diteruskan ke aplikasi mikro yang id-nya ditentukan sebagai kuncinya.

Kasus Non- iframe

Seperti disebutkan di atas, menghosting aplikasi mikro di iframe s memang memiliki beberapa kelemahan. Ada dua alternatif: sertakan langsung sebaris di HTML laman, atau tempatkan di DOM bayangan. Kedua alternatif mencerminkan pro dan kontra dari iframe s, tetapi terkadang dengan cara yang berbeda.

Misalnya, kebijakan CSP microapp individual harus digabungkan. Teknologi bantu seperti pembaca layar harus bekerja lebih baik daripada iframe s, dengan asumsi mereka mendukung shadow DOM (yang belum semuanya). Seharusnya mudah mengatur untuk mendaftarkan pekerja layanan microapp menggunakan konsep "ruang lingkup" pekerja layanan, meskipun aplikasi harus memastikan bahwa pekerja layanannya terdaftar di bawah nama aplikasi, bukan "/" . Tidak ada masalah tata letak yang terkait dengan iframe yang berlaku untuk metode DOM sebaris atau bayangan.

Namun, aplikasi yang dibangun menggunakan kerangka kerja seperti Angular dan React cenderung tidak bahagia hidup sebaris atau dalam bayangan DOM. Untuk itu, kami cenderung ingin menggunakan iframe s.

Metode inline dan shadow DOM berbeda dalam hal CSS. CSS akan dienkapsulasi dengan rapi dalam shadow DOM. Jika karena alasan tertentu kami ingin berbagi CSS luar dengan shadow DOM, kami harus menggunakan lembar gaya yang dapat dibangun atau yang serupa. Dengan aplikasi mikro sebaris, semua CSS akan dibagikan di seluruh halaman.


Pada akhirnya, mengimplementasikan logika untuk aplikasi mikro DOM inline dan shadow di <yumcha-portal> sangatlah mudah. Kami mengambil konten untuk aplikasi mikro tertentu dari tempat konten tersebut dimasukkan ke dalam halaman oleh logika server sebagai elemen <template> HTML, mengkloningnya, lalu menambahkannya ke apa yang disebut renderRoot , yang biasanya merupakan shadow DOM elemen, tetapi dapat juga disetel ke elemen itu sendiri ( this ) untuk kasus inline (non-shadow DOM).

Tapi tunggu! Konten yang disajikan oleh server microapp adalah seluruh halaman HTML. Kita tidak bisa menyisipkan halaman HTML untuk microapp, lengkap dengan tag html , head , dan body , ke tengah untuk macroapp, bukan?

Kami memecahkan masalah ini dengan memanfaatkan kekhasan dari tag template di mana konten microapp yang diambil dari server microapp dibungkus. Ternyata ketika browser modern menemukan tag template , meskipun mereka tidak "mengeksekusinya", mereka menguraikannya , dan dengan melakukan itu mereka menghapus konten yang tidak valid seperti <html> , <head> , dan <body> , sambil mempertahankan konten dalamnya. Jadi <script> dan <link> di <head> , serta konten <body> , dipertahankan. Inilah yang kami inginkan untuk tujuan memasukkan konten microapp ke halaman kami.

Arsitektur Mikro-frontend: Setan Ada dalam Detailnya

Frontend mikro akan berakar di ekosistem aplikasi web jika (a) ternyata menjadi pendekatan arsitektur yang lebih baik, dan (b) kita dapat mengetahui cara mengimplementasikannya dengan cara yang memenuhi berbagai persyaratan praktis web.

Dalam hal pertanyaan pertama, tidak ada yang mengklaim bahwa antarmuka mikro adalah arsitektur yang tepat untuk semua kasus penggunaan. Secara khusus, akan ada sedikit alasan untuk pengembangan greenfield oleh satu tim untuk mengadopsi mikro frontend. Saya akan meninggalkan pertanyaan tentang jenis aplikasi apa dalam jenis konteks apa yang paling diuntungkan dari pola mikro-frontend ke komentator lain.

Dalam hal implementasi dan kelayakan, kami telah melihat ada banyak detail yang perlu diperhatikan, termasuk beberapa yang bahkan tidak disebutkan dalam artikel ini—terutama otentikasi dan keamanan, duplikasi kode, dan SEO. Meskipun demikian, saya berharap artikel ini memaparkan pendekatan implementasi dasar untuk frontend mikro yang, dengan penyempurnaan lebih lanjut, dapat memenuhi persyaratan dunia nyata.

Bibliografi

  • Ujung Depan Mikro — Melakukannya dengan Gaya Sudut — Bagian 1
  • Ujung Depan Mikro — Melakukannya dengan Gaya Sudut — Bagian 2
  • Mengembangkan aplikasi AngularJS menggunakan microfrontends
  • Mikro-Frontend
  • Layanan mikro UI — membalikkan anti-pola (mikro frontend)
  • Layanan mikro UI — anti-pola?
  • Pembuatan Halaman menggunakan Micro-Frontends mengambil pendekatan mirip Yumcha dari proxy terbalik dan SSI, yang sangat saya rekomendasikan.
  • Sumber daya mikro-frontend
  • Mimbar
  • Saya Tidak Mengerti Micro-Frontends. Ini adalah ikhtisar yang cukup bagus tentang jenis arsitektur mikro-frontend dan kasus penggunaan.
  • Mikro-frontend tanpa server menggunakan Vue.js, AWS Lambda, dan Hypernova
  • Micro Frontends: Ikhtisar yang bagus dan komprehensif.