如何實現多個應用之間的資源共享?javascript
以前比較多的處理方式是npm包形式抽離和引用,好比多個應用項目之間,可能有某業務邏輯模塊或者其餘是可複用的,便抽離出來以npm包的形式進行管理和使用。但這樣卻帶來了如下幾個問題:html
這些問題讓咱們意識到,擴展前端開發規模以便於多個團隊能夠同時開發一個大型且複雜的產品是一個重要但又棘手的難題。前端
所以,早在2016年,微前端概念誕生了。vue
Micro Frontends: https://micro-frontends.org/ 官網定義了微前端概念:java
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently.node
從 Micro Frontends 官網能夠了解到,微前端概念是從微服務概念擴展而來的,摒棄大型單體方式,將前端總體分解爲小而簡單的塊,這些塊能夠獨立開發、測試和部署,同時仍然聚合爲一個產品出如今客戶面前。能夠理解微前端是一種將多個可獨立交付的小型前端應用聚合爲一個總體的架構風格。react
值得留意的幾個點:webpack
對比了npm包方式抽離,讓咱們意識到更新流程和效率的重要性。微前端因爲是多個子應用的聚合,若是多個業務應用依賴同一個服務應用的功能模塊,只須要更新服務應用,其餘業務應用就能夠立馬更新,從而縮短了更新流程和節約了更新成本。git
遷移是一項很是耗時且艱難的任務,好比有一個管理系統使用 AngularJS 開發維護已經有三年時間,可是隨時間的推移和團隊成員的變動,不管從開發成本仍是用人需求上,AngularJS 已經不能知足要求,因而團隊想要更新技術棧,想在其餘框架中實現新的需求,可是現有項目怎麼辦?直接遷移是不可能的,在新的框架中徹底重寫也不太現實。github
使用微前端架構就能夠解決問題,在保留原有項目的同時,能夠徹底使用新的框架開發新的需求,而後再使用微前端架構將舊的項目和新的項目進行整合。這樣既可使產品獲得更好的用戶體驗,也可使團隊成員在技術上獲得進步,產品開發成本也降到的最低。
在目前的單頁應用架構中,使用組件構建用戶界面,應用中的每一個組件或功能開發完成或者bug修復完成後,每次都須要對整個產品從新進行構建和發佈,任務耗時操做上也比較繁瑣。
在使用了微前端架構後,能夠將不能的功能模塊拆分紅獨立的應用,此時功能模塊就能夠單獨構建單獨發佈了,構建時間也會變得很是快,應用發佈後不須要更改其餘內容應用就會自動更新,這意味着你能夠進行頻繁的構建發佈操做了。
由於微前端構架與框架無關,當一個應用由多個團隊進行開發時,每一個團隊均可以使用本身擅長的技術棧進行開發,也就是它容許適當的讓團隊決策使用哪一種技術,從而使團隊協做變得再也不僵硬。
自組織模式:經過約定進行互調,但會遇處處理第三方依賴等問題。
基座模式:經過搭建基座、配置中心來管理子應用。如基於SIngle Spa的偏通用的乾坤方案,也有基於自己團隊業務量身定製的方案。
去中心模式:脫離基座模式,每一個應用之間均可以彼此分享資源。如基於Webpack 5 Module Federation實現的EMP微前端方案,能夠實現多個應用彼此共享資源分享。
其中,目前值得關注是去中心模式中的EMP微前端方案,既能夠實現跨技術棧調用,又能夠在相同技術棧的應用間深度定製共享資源,若是剛開始調研微前端的話,能夠先嚐試瞭解一下EMP微前端方案,或許會給你帶來不錯的使用體驗
Systemjs:https://github.com/systemjs/systemjs
在微前端架構中,微應用被打包爲模塊,但瀏覽器不支持模塊化,須要使用 systemjs 實現瀏覽器中的模塊化。
systemjs 是一個用於實現模塊化的 JavaScript 庫,有屬於本身的模塊化規範。
在開發階段咱們可使用 ES 模塊規範,而後使用 webpack 將其轉換爲 systemjs 支持的模塊。
案例:經過 webpack 將 react 應用打包爲 systemjs 模塊,在經過 systemjs 在瀏覽器中加載模塊
npm install webpack@5.17.0 webpack-cli@4.4.0 webpack-dev-server@3.11.2 html-webpack-plugin@4.5.1 @babel/core@7.12.10 @babel/cli@7.12.10 @babel/preset-env@7.12.11 @babel/preset-react@7.12.10 babel-loader@8.2.2
package.json
{
"name": "systemjs-react",
"scripts": {
"start": "webpack serve"
},
"dependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"babel-loader": "^8.2.2",
"html-webpack-plugin": "^4.5.1",
"webpack": "^5.17.0",
"webpack-cli": "^4.4.0",
"webpack-dev-server": "^3.11.2"
}
}
複製代碼
webpack.config.js
const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
mode: "development",
entry: "./src/index.js", // 入口
output: { // 出口
// 打包目錄及文件
path: path.join(__dirname, "build"),
filename: "index.js",
// 指定構建時所須要的庫
libraryTarget: "system"
},
devtool: "source-map",
// 服務器運行配置
devServer: {
port: 9000, // 端口
// 靜態資源文件夾
contentBase: path.join(__dirname, "build"),
historyApiFallback: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
// 對應語法轉換
presets: ["@babel/preset-env", "@babel/react"]
}
}
}
]
},
plugins: [ // 插件
new HtmlWebpackPlugin({
/* 打包時,不須要自動引入JS文件(<script> 標籤) */
inject: false,
/* 使用微前端的方式,咱們須要本身加載對應的 JS 文件 */
template: "./src/index.html"
})
],
// 添加打包排除選項,微前端中須要使用公共的 React ,打包是不須要的
externals: ["react", "react-dom", "react-router-dom"]
}
複製代碼
src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>systemjs-react</title>
<!-- 按照 systemjs 模塊化的方式引入React框架應用 -->
<script type="systemjs-importmap"> { "imports": { "react": "https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js", "react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js", "react-router-dom": "https://cdn.jsdelivr.net/npm/react-router-dom@5.2.0/umd/react-router-dom.min.js" } } </script>
<!-- systemjs 庫 -->
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.0/dist/system.min.js"></script>
</head>
<body>
<div id="root"></div>
<script> // 按照 systemp 的方式,引入具體應用 System.import("./index.js") </script>
</body>
</html>
複製代碼
src/index.js
import React from "react"
import ReactDom from "react-dom"
import App from './App.js'
ReactDom.render(<App />, document.getElementById("root"))
複製代碼
src/App.js
import React from "react"
export default function App(){
return <div>React micro for systemjs</div>
}
複製代碼