微前端是一种将单体前端应用拆分成多个小型、松耦合的子应用的架构模式。它通过将前端应用拆分成多个独立的部分,可以使团队在开发、测试和部署等方面更加灵活和高效。同时,微前端架构还具有技术栈无关性、系统扩展性和可维护性等优点。在微前端架构中,不同的子应用可以使用不同的技术栈,通过消息通信等机制实现互相的调用和协作。通过微前端架构,可以有效地提高应用的可维护性、可扩展性和团队协作效率。
1. 微前端的实现方式
1.1 iframe 方式
使用 iframe 技术来实现微前端,每个子应用都可以独立部署和运行,子应用通过 iframe 嵌入到主应用中。主应用和子应用之间通过约定的协议进行通信,比如 postMessage、localStorage、cookie 等。
示例代码
主应用 index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>主应用</title>
</head>
<body>
<h1>主应用</h1>
<div id="app"></div>
<script>
// 加载子应用
function loadMicroApp(url) {
const iframe = document.createElement('iframe');
iframe.src = url;
iframe.style.width = '100%';
iframe.style.height = '500px';
document.getElementById('app').appendChild(iframe);
}
</script>
</body>
</html>
子应用 subapp.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>子应用</title>
</head>
<body>
<h1>子应用</h1>
<script>
// 发送消息到主应用
window.parent.postMessage('Hello, 主应用!', '*');
// 监听主应用发送的消息
window.addEventListener('message', function(event) {
console.log('收到来自主应用的消息:', event.data);
});
</script>
</body>
</html>
1.2 Web Components 方式
使用 Web Components 技术来实现微前端,每个子应用都打包为一个 Web Component,子应用通过自定义元素嵌入到主应用中。
示例:
主应用 index.html:
<!DOCTYPE html>
<html>
<head>
<title>Web Components Example</title>
</head>
<body>
<h1>Main App</h1>
<micro-app-one></micro-app-one>
<micro-app-two></micro-app-two>
<script src="index.js"></script>
</body>
</html>
主应用 index.js:
// 定义子应用 URL
const microAppOneUrl = 'http://localhost:8081/micro-app-one.js';
const microAppTwoUrl = 'http://localhost:8082/micro-app-two.js';
// 加载子应用
async function loadMicroApp(url, tagName) {
const { default: MicroApp } = await import(url);
customElements.define(tagName, MicroApp);
}
loadMicroApp(microAppOneUrl, 'micro-app-one');
loadMicroApp(microAppTwoUrl, 'micro-app-two');
// 绑定事件监听器
window.addEventListener('message', (event) => {
if (event.origin !== window.location.origin) {
return;
}
if (event.data.type === 'message-from-micro-app-one') {
console.log('Received message from micro-app-one:', event.data.message);
}
if (event.data.type === 'message-from-micro-app-two') {
console.log('Received message from micro-app-two:', event.data.message);
}
});
子应用 micro-app-one.js:
class MicroAppOne extends HTMLElement {
connectedCallback() {
this.innerHTML = '<h2>Micro App One</h2>';
const button = document.createElement('button');
button.textContent = 'Send message to main app';
button.addEventListener('click', () => {
window.parent.postMessage(
{
type: 'message-from-micro-app-one',
message: 'Hello from micro-app-one!',
},
'*'
);
});
this.appendChild(button);
}
}
export default MicroAppOne;
子应用 micro-app-two.js:
class MicroAppTwo extends HTMLElement {
connectedCallback() {
this.innerHTML = '<h2>Micro App Two</h2>';
const button = document.createElement('button');
button.textContent = 'Send message to main app';
button.addEventListener('click', () => {
window.parent.postMessage(
{
type: 'message-from-micro-app-two',
message: 'Hello from micro-app-two!',
},
'*'
);
});
this.appendChild(button);
}
}
export default MicroAppTwo;
在这个示例中,我们定义了两个子应用的 URL,并在主应用的 index.js 文件中加载并注册了这两个子应用。然后,在主应用的 index.html 文件中,我们使用自定义元素的方式引入了这两个子应用,并在主应用的 DOM 中展示它们。同时,在主应用的 index.js 文件中,我们监听了来自子应用的消息,并进行了相应的处理。
在每个子应用的 JavaScript 文件中,我们定义了一个自定义元素,并在其中添加了一个按钮。当用户点击按钮时,子应用会向主应用发送一条消息,并通过 postMessage() 方法将消息发送到主应用的窗口中。主应用在监听到这些消息后,根据消息的类型进行相应的处理,并将消息输出到浏览器的控制台中。
这个示例中的通信方式是基于 postMessage() 方法实现的。每个子应用可以向主应用发送一条消息,主应用则可以通过监听 message 事件来接收这些消息。通过这种方式,我们可以在微前端架构中实现子应用与主应用之间的通信。
除了 postMessage() 方法,还有许多其他的通信方式可以用于微前端架构。例如,我们可以使用事件总线、WebSocket 等技术来实现子应用与主应用之间的通信。
1.3 single-spa
single-spa 是一个用于实现微前端的 JavaScript 框架,它可以让我们在不同的框架和库之间无缝切换,同时也提供了很多开箱即用的功能,例如路由管理、应用生命周期管理、异步加载等。
使用 single-spa 可以非常方便地实现微前端架构,下面是一个简单的使用示例:
// 在主应用中注册微应用
singleSpa.registerApplication(
'micro-app',
() => import('./micro-app'),
(location) => location.pathname.startsWith('/micro-app')
);
// 启动 single-spa
singleSpa.start();
这段代码的意思是,在主应用中注册了一个名为 micro-app 的微应用,它的入口文件是 ./micro-app,当用户访问的路径以 /micro-app 开头时,就加载该微应用。最后,调用 singleSpa.start() 方法启动 single-spa。
在子应用中,我们需要对输出的 bundle 进行一些配置,例如将 bundle 暴露为一个全局变量,以便主应用加载和调用,下面是一个示例:
// webpack 配置
module.exports = {
// ...
output: {
library: 'microApp',
libraryTarget: 'window',
// ...
},
// ...
};
这段代码的意思是,在 webpack 的配置文件中,将输出的 bundle 暴露为一个名为 microApp 的全局变量,并将其绑定到 window 对象上。
在子应用的代码中,我们可以定义一些生命周期函数,例如 bootstrap、mount 和 unmount,这些函数会在子应用被加载、挂载和卸载时自动调用,下面是一个示例:
export const bootstrap = async () => {
console.log('micro-app bootstrap');
};
export const mount = async () => {
console.log('micro-app mount');
};
export const unmount = async () => {
console.log('micro-app unmount');
};
这段代码的意思是,在子应用中定义了三个生命周期函数,分别是 bootstrap、mount 和 unmount,它们会在不同的阶段被调用,我们可以在这些函数中执行一些初始化操作和清理操作。
1.4 qiankun
qiankun 是一个基于 single-spa 的微前端解决方案,它提供了更多的功能和优化,例如多实例支持、样式隔离、事件通信等。
下面是一个简单的使用示例:
// 在主应用中注册微应用
const microApp = {
name: 'micro-app',
entry: '//localhost:8081',
container: '#micro-app',
activeRule: '/micro-app',
};
qiankun.registerMicroApps([microApp]);
// 启动 qiankun
qiankun.start();
这段代码的意思是,在主应用中注册了一个名为 micro-app 的微应用,它的入口 URL 是 //localhost:8081,容器选择器为 #micro-app,当用户访问的路径以 /micro-app 开头时,就加载该微应用。最后,调用 qiankun.start() 方法启动 qiankun。
在子应用中,我们需要对输出的 bundle 进行一些配置,例如将 bundle 暴露为一个全局变量,以便主应用加载和调用,下面是一个示例:
// webpack 配置
module.exports = {
// ...
output: {
library: 'microApp',
libraryTarget: 'umd',
// ...
},
// ...
};
这段代码的意思是,在 webpack 的配置文件中,将输出的 bundle 暴露为一个名为 microApp 的全局变量,并将其绑定到 umd 模块上。
在子应用的代码中,我们可以定义一些生命周期函数,例如 bootstrap、mount 和 unmount,这些函数会在子应用被加载、挂载和卸载时自动调用,下面是一个示例:
export const bootstrap = async () => {
console.log('micro-app bootstrap');
};
export const mount = async () => {
console.log('micro-app mount');
};
export const unmount = async () => {
console.log('micro-app unmount');
};
这段代码的意思是,在子应用中定义了三个生命周期函数,分别是 bootstrap、mount 和 unmount,它们会在不同的阶段被调用,我们可以在这些函数中执行一些初始化操作和清理操作。
除了生命周期函数之外,qiankun 还提供了一些 API 用于实现跨应用通信和路由管理,例如 setGlobalState、onGlobalStateChange、start 等,具体用法可以参考官方文档。
2. 微前端的优化
- 按需加载:在微前端架构中,由于每个子应用都是独立的,因此可以采用按需加载的策略,根据需要加载和渲染子应用的页面组件,从而减少资源的重复请求和加载,提高页面的加载速度和用户体验。
- 缓存策略:对于一些静态资源,可以采用缓存策略来减少资源的重复请求和加载,从而优化页面的性能。可以使用浏览器缓存、CDN缓存等方式来缓存静态资源。
- 资源管理:在微前端架构中,不同的子应用之间可能会存在资源冲突和命名空间污染问题,因此需要采用资源管理的策略来避免这些问题。可以使用模块化的方式来管理每个子应用的资源,避免命名冲突和重复加载。
- 代码分割:由于每个子应用都是独立的,因此可以将每个子应用的代码进行分割,从而减小每个子应用的代码体积,提高应用的性能和用户体验。可以使用webpack等构建工具来进行代码分割。
- 监控和调试:在微前端架构中,需要对子应用和主应用的运行情况进行监控和调试,以便及时发现和解决问题,提高应用的稳定性和可靠性。可以使用浏览器的开发者工具、日志记录工具、性能监控工具等来进行监控和调试。
- 共享依赖:在微前端架构中,可能会存在多个子应用共用同一个依赖库的情况。为了避免重复加载和占用资源,可以将这些依赖库提取出来,形成一个公共库,并通过CDN等方式进行共享。
- 部署优化:在微前端架构中,需要进行多个子应用的部署和协调。为了减少部署的复杂性和时间,可以使用容器化技术(如Docker)来进行部署,从而实现快速部署和协调。
3 应用场景
微前端适用于以下场景:
-
大型单页应用:当应用规模较大时,拆分为多个小型子应用可以提高应用的可维护性和可扩展性。
-
多团队协作开发:当多个团队协作开发一个应用时,拆分为多个小型子应用可以提高团队间的合作效率和减少代码冲突。
-
跨技术栈应用:当应用需要使用不同的技术栈时,可以将应用拆分为多个小型子应用,每个子应用使用不同的技术栈。
-
应用复用:当多个应用需要共享相同的功能模块时,可以将功能模块拆分为独立的子应用,便于在不同的项目中重复使用。
-
独立更新:当应用需要进行更新时,可以只更新其中一个子应用,而不影响其他子应用的运行。