【架构】实现微前端架构

实现微前端架构

相关文章

实现微前端架构


一、路由分发式

即通过路由将不同的业务分发到不同的独立前端应用上

方式

通过HTTP服务器的反向代理

缺点

  • 一个页面无法聚合多个应用
  • 应用切换会使页面刷新

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 80;
server_name example.com;

location /app1 {
proxy_pass http://localhost:3001; # 将 /app1 的请求转发到端口号为 3001 的前端应用
}

location /app2 {
proxy_pass http://localhost:3002; # 将 /app2 的请求转发到端口号为 3002 的前端应用
}

location /app3 {
proxy_pass http://localhost:3003; # 将 /app3 的请求转发到端口号为 3003 的前端应用
}

# 可以添加更多的业务和对应的前端应用配置
}

访问

现在,当用户访问 http://example.com/app1 时,Nginx 会将请求转发到端口号为 3001 的前端应用;访问 http://example.com/app2 时,会转发到端口号为 3002 的前端应用;以此类推。

二、微应用化

在开发时应用都是以单一、微小应用的形式存在,而在运行时则通过构建系统合并这些应用并组成成一个新的应用

方式

通过构建系统组合式集成

缺点

  • 只能使用一个前端框架

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// build/mergeApps.js

const webpack = require('webpack');
const { merge } = require('webpack-merge');
const path = require('path');

const commonConfig = {
mode: 'production',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../dist'),
},
module: {
rules: [
// 添加对应的加载器规则,处理不同类型的文件
// ...
],
},
plugins: [
// 添加相应的插件,处理各种情况下的优化和处理
// ...
],
};

const app1Config = {
entry: {
app1: './app1/index.js',
},
};

const app2Config = {
entry: {
app2: './app2/index.js',
},
};

const mergedConfig = merge(commonConfig, app1Config, app2Config);

webpack(mergedConfig, (err, stats) => {
if (err || stats.hasErrors()) {
console.error(err || stats.toString());
process.exit(1);
}
console.log('Apps merged successfully!');
});

访问

完成合并打包后,在 dist 目录下会生成合并后的新应用文件。将这些文件部署到服务器上,然后通过服务器访问新的应用即可。

三、微件化

将编译好的代码部署到指定的服务器上,运行时加载相应的业务模块即可

方式

如webpack构建出的trunk

缺点

  • 实施成本高

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function loadModule(moduleName) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = `http://example.com/${moduleName}.js`; // 根据模块名动态生成对应的文件路径
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}

// 加载模块1
loadModule('module1').then(() => {
console.log('Module 1 loaded successfully');
}).catch((error) => {
console.error('Failed to load module 1:', error);
});

// 加载模块2
loadModule('module2').then(() => {
console.log('Module 2 loaded successfully');
}).catch((error) => {
console.error('Failed to load module 2:', error);
});

// 加载模块3
loadModule('module3').then(() => {
console.log('Module 3 loaded successfully');
}).catch((error) => {
console.error('Failed to load module 3:', error);
});

访问

loadModule 函数根据模块名动态创建 <script> 标签,并将其添加到文档头部,从而加载对应的模块。当模块加载完成时,会触发 onload 回调,如果加载失败则触发 onerror 回调。

四、前端容器

在当前页面中创建一个容器承载另外一个前端页面

方式

iframe

缺点

  • 路由状态丢失,刷新一下,iframe的url状态就丢失了
  • dom割裂严重,弹窗只能在iframe内部展示,无法覆盖全局

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Iframe</title>
</head>
<body>
<h1>Main Page</h1>
<p>This is the main page.</p>

<!-- 创建 iframe 容器 -->
<iframe id="iframe-container" width="600" height="400" frameborder="0"></iframe>

<button onclick="loadPage('page1.html')">Load Page 1</button>
<button onclick="loadPage('page2.html')">Load Page 2</button>
<button onclick="loadPage('page3.html')">Load Page 3</button>

<script>
function loadPage(pageUrl) {
const iframe = document.getElementById('iframe-container');
iframe.src = pageUrl;
}
</script>
</body>
</html>

五、结合web components构建

将当前应用构建成一个web components组件,并在其他支持引入web components组件的框架中使用

方式

  • Angular6+版本支持转换
  • Vue3.2+版本支持转换

缺点

  • 浏览器支持度低

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// app1-component.js
class App1Component extends HTMLElement {
connectedCallback() {
this.innerHTML = `<h1>App 1</h1>`;
}
}
customElements.define('app1-component', App1Component);

// app2-component.js
class App2Component extends HTMLElement {
connectedCallback() {
this.innerHTML = `<h1>App 2</h1>`;
}
}
customElements.define('app2-component', App2Component);

// app3-component.js
class App3Component extends HTMLElement {
connectedCallback() {
this.innerHTML = `<h1>App 3</h1>`;
}
}
customElements.define('app3-component', App3Component);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 主应用页面
const router = {
'/app1': 'app1-component',
'/app2': 'app2-component',
'/app3': 'app3-component'
};

function loadComponent(route) {
const componentName = router[route];
if (!componentName) {
console.error(`No component found for route ${route}`);
return;
}

const appContainer = document.getElementById('app-container');
appContainer.innerHTML = '';

// 动态创建并添加对应的 Web Component
const component = document.createElement(componentName);
appContainer.appendChild(component);
}

// 监听路由变化
window.addEventListener('hashchange', () => {
const route = window.location.hash.substr(1); // 获取路由,去掉 #
loadComponent(route);
});

// 初始加载默认路由对应的组件
loadComponent(window.location.hash.substr(1));

六、前端微服务化

即前端应用都是完全独立(技术栈、开发、部署、构建)、自主运行的,最后通过模块化的方式组合出完整的前端应用

方式

  • 在页面合适的地方引入或者创建DOM;
  • 用户操作时加载对应的应用并能完成卸载

缺点

  • 同一个多个应用之间可能有依赖冲突

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modular Frontend</title>
</head>
<body>
<h1>Main Frontend Application</h1>
<p>This is the main frontend application.</p>

<div id="app-container"></div>

<script>
function loadModule(moduleName) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = `${moduleName}.js`; // 每个模块单独打包为一个 JavaScript 文件
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}

// 加载独立前端应用的模块
Promise.all([
loadModule('app1'),
loadModule('app2'),
loadModule('app3')
]).then(() => {
console.log('All modules loaded successfully');
// 在这里可以组合各个模块,形成完整的前端应用
}).catch((error) => {
console.error('Failed to load module:', error);
});
</script>
</body>
</html>

【架构】实现微前端架构
https://www.cccccl.com/20240201/架构/实现微前端架构/
作者
Jeffrey
发布于
2024年2月1日
许可协议