使用開源微前端框架 Luigi 建立一個基於微前端架構的工程
微前端通常被稱為“前端微服務”。 它們允許您將大型單體前端分解為獨立的、可擴充套件的、可以協同工作的獨立部分。 微前端架構對於複雜的產品或擁有眾多團隊的公司尤其有用,可以幫助您建立一致的 Web 應用程式。
觀看此視訊,其中解釋了微前端架構的基礎知識以及如何使用 Luigi 實現。
Luigi 是一個用 Svelte 編寫的開源微前端框架。 它允許您建立一致的使用者介面和導航,同時還提供其他功能以使開發更容易。 它的外觀基於基本庫樣式。 Luigi 與技術無關,允許您使用 React、Angular、UI5 或任何其他技術構建應用程式並向其新增微前端。
Luigi Fiddle 是一個測試遊樂場,您可以在其中感受 Luigi。
這張圖描繪了 Luigi 的基本架構:
Luigi terminology
這是一小部分 Luigi 術語:
-
Luigi Core - 指其中顯示微前端的主要應用程式。 它包括頂部和側面導航以及與主應用程式相關的所有其他設定。
-
Luigi Client - 這個術語涵蓋了 Luigi 提供的所有與微前端相關的設定。 微前端可通過 Luigi Client API 進行配置。
-
parameters - 引數是用於配置 Luigi 應用程式的元素。
-
導航節點 - Luigi 中側面導航的各個連結。
-
Contexts - 上下文是 Luigi 引數,允許您將物件傳遞到微前端。
-
Views - 檢視,微前端的另一個名稱。
本系列文章,我們將從頭開始使用 Luigi 建立一個應用程式。它將基於網路購物平臺的理念,並將包括其他功能,例如用於以英語和德語顯示網站的本地化。
該應用程式由三個主要部分組成:
-
使用 React 構建的 Luigi Core 應用程式:“主應用程式”,其中包含微前端幷包括無論您導航到應用程式的哪個子頁面都保持一致的頂部和側面導航。
-
使用 React 構建的微前端:它們包括主頁、“產品”頁面和包含每個產品資訊的“產品詳細資訊”列表。
-
使用 UI5 構建的微前端:“訂單歷史”頁面,顯示已購買產品的數量和價格。
最後,您完成的應用程式的主頁應如下所示:
完整原始碼
命令列 npm i create-react-app:
然後是命令列:npx create-react-app react-core-mf
Install dependencies
If you haven’t already done so, make sure you install SAP Fonts.
跳轉到 core app 去:cd react-core-mf
安裝依賴:
npm i -P @luigi-project/core @luigi-project/client fundamental-styles fundamental-react @sap-theming/theming-base-content react-router-dom
npm i copy-webpack-plugin webpack webpack-cli @babel/core @babel/preset-env babel-loader --save-dev
Create UI5 micro-frontend
建立一個 ui5-mf 資料夾:
命令列建立 ui5 工程:
npm install -g yo generator-easy-ui5
Add a file with product data
在此步驟中,您將建立一個檔案,其中包含有關您的購物應用中在售產品的資訊。
在現實的專案實現中,這些資料將由資料庫提供。但為簡單起見,您將在 .json 檔案中建立虛擬資料。該檔案將由主應用程式和微前端使用。
Navigate to ui5-mf/uimodule/webapp/model and create a products.json file with the following content:
{
"ProductCollection": [{
"id": 101,
"name": "Logitech Mouse",
"price": 45.0,
"stock": 80,
"icon": "product",
"currencyCode": "EUR",
"orderQuantity": 2,
"description": "LIGHTSPEED Wireless Gaming Mouse with HERO Sensor"
},
{
"id": 102,
"name": "Logitech Keyboard",
"price": 50.0,
"stock": 22,
"icon": "product",
"currencyCode": "EUR",
"orderQuantity": 1,
"description": "A physical keyboard that uses an individual spring and switch for each key. Today, only premium keyboards are built with key switches; however, they were also used in the past, such as in the Model M keyboard from IBM, which used buckling spring switches"
},
{
"id": 103,
"name": "HP Optical Mouse",
"price": 35.0,
"stock": 4,
"icon": "product",
"currencyCode": "EUR",
"orderQuantity": 2,
"description": "Utilizing the latest optical sensing technology, the HP USB Optical Scroll Mouse records precise motion."
},
{
"id": 104,
"name": "MacBook Pro",
"price": 1299.0,
"stock": 11,
"icon": "laptop",
"currencyCode": "EUR",
"orderQuantity": 3,
"description": "It features a touch-sensitive OLED display strip located in place of the function keys, a Touch ID sensor integrated with the power button, a butterfly mechanism keyboard similar to the MacBook, and four USB-C ports that also serve as Thunderbolt 3 ports."
},
{
"id": 105,
"name": "Magic Mouse",
"price": 40.0,
"stock": 20,
"icon": "product",
"currencyCode": "EUR",
"orderQuantity": 6,
"description": "The Magic Mouse 2 (Apple Magic Mouse 2), is a computer mouse developed and released by Apple Inc. It features a multi-touch acrylic surface for scrolling. ... The mouse features a lithium-ion rechargeable battery and Lightning connector for charging and pairing."
},
{
"id": 106,
"name": "Brother Printer",
"price": 235.0,
"stock": 24,
"icon": "fx",
"currencyCode": "EUR",
"orderQuantity": 1,
"description": "Our affordable, quality machines provide you with the optimal way to take care of all your printing needs. Shop for the right printer, all-in-one, or fax machine for your home or home office today."
},
{
"id": 107,
"name": "iPhone 11",
"price": 835.0,
"stock": 45,
"icon": "iphone",
"currencyCode": "EUR",
"orderQuantity": 8,
"description": "The iPhone 11 dimensions are 150.9mm x 75.7mm x 8.3mm (H x W x D). It weighs about 194 grams (6.84 ounces).It features a 6.1-inch all-screen LCD display and is powered by Apple new A13 bionic chip with Third-Generation Neural Engine."
},
{
"id": 108,
"name": "Google Pixel 3a",
"price": 299.0,
"stock": 54,
"icon": "desktop-mobile",
"currencyCode": "EUR",
"orderQuantity": 7,
"description": "At 5.6 inches, the Google Pixel 3a display is proportionate to the relatively small body of the phone – that is to say, it is rather small. The display is Full HD+ and OLED, with a resolution of 2220 x 1080, and because of the relatively small screen size the pixels per inch count is rather high at 441."
},
{
"id": 109,
"name": "PlayStation 4",
"price": 330.0,
"stock": 94,
"icon": "video",
"currencyCode": "EUR",
"orderQuantity": 1,
"description": "PS4 is the fourth home video game console produced by Sony Computer Entertainment and is compatible with the PlayStation 3. It was officially announced at a press conference on February 20, 2013 and launched on November 15, 2013."
},
{
"id": 110,
"name": "Dell Monitor",
"price": 630.0,
"stock": 20,
"icon": "sys-monitor",
"currencyCode": "EUR",
"orderQuantity": 3,
"description": "34'' U3419W Monitor, Display with stand Height adjustable (115 mm), tiltable (-5° to 21°), rotatable (-30° to 30°) Security slot (cable lock sold separately), anti-theft slot for locking to stand (for display). Includes: DisplayPort cable, HDMI cable, Power cable, Stand, USB 3.0 Type-A to Type-B cable, USB-C cable"
}
]
}
Prepare React app
此步驟為您的開發做好準備。 為了能夠使用 webpack 並完全控制您的 React 應用程式,您需要觸發 npm run eject 命令。
cd react-core-mf
執行下列命令列。 請注意,由於此命令的工作方式,npm run eject 可能會失敗。如果出現錯誤,則需要在執行命令之前提交任何更改。
npm run eject
如果遇到下面的錯誤訊息:
解決方案:
- git add .
- git commit -am "Save before ejecting"
之後重新執行 eject 命令,就成功了:
Add Luigi to index.html
In this step, you will let Luigi take control of the index.hmtl file - the entry point for your app.
編輯 react-core-mf/public/index.html 檔案的內容為:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Luigi</title>
<link rel="stylesheet" href="/luigi-core/luigi.css" />
</head>
<body>
<script src="/luigi-core/luigi.js"></script>
<script src="/luigi-config.js"></script>
</body>
</html>
Create micro-frontends template
在這一步中,您將建立另一個 HTML 檔案,該檔案將作為 React 建立 React 微前端的模板。
轉到 react-core-mf/public 並建立一個名為 app.html 的新檔案。 將此程式碼貼上到檔案中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
Configure webpack
在這一步中,我們配置 webpack 並調整依賴項,以便更輕鬆地開發和構建應用程式。
修改 react-core-mf/config/webpack-config.js:
加上一行:
const CopyWebpackPlugin = require('copy-webpack-plugin');
刪除這一行:
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
以及:
新的 plugin:
new CopyWebpackPlugin(
{
patterns: [
{
context: 'public',
from: 'index.html',
to: 'index.html'
},
{
from: 'node_modules/@luigi-project/core',
to: './luigi-core'
}
]
},
{
ignore: ['.gitkeep', '**/.DS_Store', '**/Thumbs.db'],
debug: 'warning'
}
),
new HtmlWebpackPlugin(
{
inject: true,
template: __dirname + '/../public/app.html',
filename: 'app.html'
}
),
Create Luigi configuration file
在這一步中,您將建立一個 Luigi 配置檔案。 這是任何 Luigi 應用程式的中心點。 它允許您配置一致的導航和許多其他 Luigi 功能。
react-core-mf/public 目錄下面新建一個配置檔案:luigi-config.js.
在現實生活中,您可以為配置檔案指定任何您想要的名稱或建立多個檔案。 唯一重要的是使用正確的 Luigi 引數和語法,這將在下一步中介紹。
Configure Luigi for "Home" node
藉助與導航和常規設定有關的簡單引數,您將建立您的第一個“主頁”導航節點並使您的應用程式具有響應性。
這些是您將使用的 Luigi 導航引數:
- pathSegment - 新增到 URL 的文字段
- label - 導航中顯示的節點的名稱
- icon - 標籤旁邊顯示的 SAP 圖示
- viewUrl - 微前端的 URL
使用下面的程式碼,您將為您的頁面配置標題,並使您的應用程式在移動裝置上使用 responseNavigation 引數看起來更好:
/* eslint-disable no-undef */
Luigi.setConfig({
navigation: {
nodes: () => [
{
pathSegment: 'home',
label: 'Home',
icon: 'home',
viewUrl: '/app.html#/home'
}
]
},
settings: {
header: {
title: 'Luigi Application',
logo: '/logo.png'
},
responsiveNavigation: 'simpleMobileOnly'
}
}
);
Create "Home" view
在這一步中,您將使用 React 建立您的第一個微前端(又名檢視)。 這是一個帶有歡迎資訊的簡單“主頁”檢視。
導航到 react-core-mf/src 並建立一個名為 views 的資料夾。
新建一個 Home.jsx 檔案:
import React from 'react';
import { LayoutPanel } from 'fundamental-react';
export const Home = () => {
return (
<LayoutPanel>
<LayoutPanel.Body>
<h2>Welcome to Luigi - a micro-frontend framework</h2>
</LayoutPanel.Body>
</LayoutPanel>
);
}
Configure router for "Home" view
在此步驟中,您將對 React 應用程式的入口點 index.js 進行更改。您將為上一步中建立的“主頁”檢視配置路由器,並匯入 Luigi Client。
開啟 react-core-mf/src/index.js 並將其內容更改為:
import React, { Component } from 'react';
import { render } from 'react-dom';
import { addInitListener } from '@luigi-project/client';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { Home } from './views/Home.jsx';
import './index.css';
class App extends Component {
constructor(props) {
super(props);
addInitListener(() => {
console.log('Luigi Client initialized.');
});
}
render() {
return (
<Router basename={`/app.html#`}>
<Route path="/home" component={Home} />
</Router>
);
}
}
render(<App />, document.getElementById('root'));
Create more views with React
在這一步中,您將建立更多 React 微前端,包括產品列表和產品詳細資訊。
導航到 react-core-mf/src/views 並建立一個包含以下內容的檔案 List.jsx:
import React from 'react';
import { MessageStrip, Avatar, LayoutPanel, LayoutGrid } from 'fundamental-react';
const NO_AVAILABLE_PRODUCT_MSG = 'Unfortunately, there is no available product at this moment.';
const panelStyle = { cursor: 'pointer' };
export const List = ({ items }) => (
(items.length === 0) ? <MessageStrip type='error'>{NO_AVAILABLE_PRODUCT_MSG}</MessageStrip>
: items.map(({id, name, price, icon, stock}) => {
return (
<LayoutPanel key={id} style={panelStyle}>
<LayoutPanel.Header>
<LayoutPanel.Head title={name} />
</LayoutPanel.Header>
<LayoutPanel.Body>
<LayoutGrid cols={2}>
<div>
<div>Price: €{price}</div>
<div>Stocks: {stock}</div>
</div>
<div><Avatar circle glyph={icon} size='s' /></div>
</LayoutGrid>
</LayoutPanel.Body>
</LayoutPanel>
)
})
);
Products.jsx:
import React from 'react';
import { List } from './List.jsx';
import { ProductCollection } from '../../../ui5-mf/uimodule/webapp/model/products.json';
import { LayoutPanel, LayoutGrid } from 'fundamental-react';
export const Products = () => (
<section className="fd-section">
<LayoutPanel>
<LayoutPanel.Header>
<h3>Items ({ProductCollection.length})</h3>
</LayoutPanel.Header>
<LayoutPanel.Body>
<LayoutGrid cols={2}>
<List items={ProductCollection} />
</LayoutGrid>
</LayoutPanel.Body>
</LayoutPanel>
</section>
);
export default Products;
Add "Products" view to Luigi app
在這一步中,您將在 Luigi 中為“產品”微前端新增一個導航節點。
編輯 react-core-mf/public/luigi-config.js:
在導航中新增一個新的“產品”節點:
navigation: {
nodes: () => [
{
pathSegment: 'home',
label: 'Home',
icon: 'home',
viewUrl: '/app.html#/home',
children: [{
pathSegment: 'products',
label: 'Products',
icon: 'list',
viewUrl: '/app.html#/products'
}]
}
]
}
Step 12: Add "Product Detail" view to Luigi app
在此步驟中,您將嚮應用程式新增 ProductDetail.jsx 檢視。 您將能夠通過 Luigi 動態引數顯示每個產品的詳細資訊,在本例中名為 :id。
檔案 luigi-config.js 的內容為:
/* eslint-disable no-undef */
Luigi.setConfig({
navigation: {
nodes: () => [
{
pathSegment: 'home',
label: 'Home',
icon: 'home',
viewUrl: '/app.html#/home',
children: [{
pathSegment: 'products',
label: 'Products',
icon: 'list',
viewUrl: '/app.html#/products'
}]
},
{
pathSegment: 'products',
label: 'Products',
icon: 'list',
viewUrl: '/app.html#/products',
keepSelectedForChildren: true,
children: [{
pathSegment: ':id',
viewUrl: '/app.html#/productDetail/:id'
}]
}
]
},
settings: {
header: {
title: 'Luigi Application',
logo: '/logo.png'
},
responsiveNavigation: 'simpleMobileOnly'
}
}
);
Step 13: Use Luigi link manager for routing
在這一步中,我們將使用 Luigi 來提供微前端的路由,而不是使用 React。 Luigi Client 的 linkManager 功能是導航到每個產品的 id 頁面的最簡單方法。
更多Jerry的原創文章,盡在:"汪子熙":