目录
在 WHAT - 微前端系列(一) 我们已经简单介绍过 qiankun。今天我们主要介绍具体实践和底层原理。
一、目标
从0到1进行一个qiankun的实践,包括分别基于vue2和vue3的项目a、项目b,让他们在主应用里能够正常加载和运行。
二、具体实践
首先,我们需要创建两个独立的 Vue.js 项目,分别用作项目A和项目B。然后,我们将使用 qiankun 将它们集成到一个主应用中。
2.1 基本使用
1. 创建 Vue.js 项目
项目A
vue create project-a
项目B
vue create project-b
2. 安装qiankun
在主应用中安装qiankun:
npm install qiankun --save
3. 在主应用中注册子应用
在主应用的入口文件(例如main.js
)中注册子应用:
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'project-a',
entry: '//localhost:8081', // 项目A的地址
container: '#project-a-container',
activeRule: '/project-a',
},
{
name: 'project-b',
entry: '//localhost:8082', // 项目B的地址
container: '#project-b-container',
activeRule: '/project-b',
},
]);
start();
4. 配置子应用
项目A的配置
在项目A的vue.config.js
中配置跨域:
module.exports = {
devServer: {
port: 8081,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
};
项目B的配置
在项目B的vue.config.js
中配置跨域:
module.exports = {
devServer: {
port: 8082,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
};
5. 运行项目
分别启动项目A和项目B:
cd project-a
npm run serve
cd project-b
npm run serve
然后启动主应用:
npm run serve
现在,您的主应用应该能够在指定的路由下加载并运行项目A和项目B了。用户可以通过访问主应用的不同路由来访问不同项目A和B的页面。在主应用中,我们已经将不同子应用的路由映射到不同的容器中:
{
name: 'project-a',
entry: '//localhost:8081', // 项目A的地址
container: '#project-a-container',
activeRule: '/project-a',
},
{
name: 'project-b',
entry: '//localhost:8082', // 项目B的地址
container: '#project-b-container',
activeRule: '/project-b',
},
因此用户只需要在浏览器地址栏中输入相应的路由路径即可访问对应的页面。假设您的主应用运行在 http://localhost:8080
,而子应用A和子应用B分别运行在 http://localhost:8081
和 http://localhost:8082
。那么用户可以按照以下方式访问不同项目的页面:
- 访问项目A的页面:在浏览器地址栏中输入
http://localhost:8080/project-a
,这将加载主应用的项目A页面。 - 访问项目B的页面:在浏览器地址栏中输入
http://localhost:8080/project-b
,这将加载主应用的项目B页面。
通过这种方式,用户可以轻松访问不同项目的页面,而主应用会根据路由规则将对应的子应用内容加载到相应的容器中,从而实现整体的微前端架构。
2.2 路由冲突
当在一个主应用中集成多个子应用时,可能会出现子应用之间的路由冲突。这是因为不同的子应用可能使用相同的路由路径来定义不同的页面,导致在主应用中无法正确地处理路由跳转。
为了解决这个问题,可以使用自定义路由解决方案。具体而言,就是在主应用中定义一个自定义的路由映射表,将子应用的路由路径映射到不同的容器中。
以下是一个简单的自定义路由解决方案的示例:
import { registerMicroApps, start } from 'qiankun';
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
// 创建一个空的 VueRouter 实例
const router = new VueRouter({
mode: 'history', // 使用 history 模式
base: window.__POWERED_BY_QIANKUN__ ? '/main-app/' : '/', // 基础路径
routes: [],
});
// 自定义路由映射表
const routeMap = {
'/project-a': {
container: '#project-a-container',
},
'/project-b': {
container: '#project-b-container',
},
};
// 动态添加路由
Object.keys(routeMap).forEach((path) => {
router.addRoute({
path,
component: {
template: `<div id="${routeMap[path].container.replace('#', '')}"></div>`,
},
});
});
// 注册子应用
registerMicroApps([
{
name: 'project-a',
entry: '//localhost:8081',
container: routeMap['/project-a'].container,
activeRule: '/project-a',
},
{
name: 'project-b',
entry: '//localhost:8082',
container: routeMap['/project-b'].container,
activeRule: '/project-b',
},
]);
start();
new Vue({
router,
render: (h) => h(App),
}).$mount('#app');
在这个示例中,我们首先创建了一个空的 VueRouter 实例,然后根据自定义路由映射表动态地添加路由。每个路由都指向一个空的 Vue 组件,它的模板中包含一个特定的容器元素,用于挂载对应子应用的内容。这样就可以在主应用中处理子应用之间的路由冲突了。
2.3 加载器
Qiankun 使用自己的加载器来加载子应用,这个加载器能够保证子应用的独立性和隔离性,避免了全局变量的污染。
当使用qiankun加载子应用时,qiankun会在主应用中创建一个独立的沙箱环境来加载和运行子应用,这样可以确保子应用的独立性和隔离性,避免了全局变量的污染。
qiankun的加载器也会负责管理子应用的生命周期和沙箱环境。
1. 具体使用示例
下面是一个简单的示例,展示了如何使用qiankun的加载器来加载子应用:
import { registerMicroApps, start } from 'qiankun';
// 注册子应用
registerMicroApps([
{
name: 'sub-app',
entry: '//localhost:8081',
container: '#sub-app-container',
activeRule: '/sub-app',
},
]);
// 启动qiankun
start();
在这个示例中,我们首先使用 registerMicroApps
方法注册了一个名为 sub-app
的子应用,然后使用 start
方法启动qiankun。qiankun会根据配置自动加载并运行注册的子应用。
2. 底层实现原理
qiankun的加载器实现了一个基于浏览器的沙箱环境,用于加载和运行子应用。其底层实现原理主要包括以下几个方面:
-
沙箱环境: qiankun使用了一种基于浏览器原生特性的沙箱环境,每个子应用都会在独立的“环境”中运行,这样可以确保子应用之间的全局变量相互隔离,不会相互污染。
-
生命周期管理: qiankun为子应用提供了丰富的生命周期钩子函数,包括
bootstrap
、mount
、unmount
等,用于管理子应用的初始化、挂载和卸载过程。在这些生命周期钩子函数中,qiankun会负责创建和销毁子应用的沙箱环境,以及处理子应用的加载和运行逻辑。
3. registerMicroApps 实现
Qiankun框架的registerMicroApps
方法是用于注册微应用的,它的作用主要有以下几点:
-
注册微应用信息: 将微应用的基本信息注册到Qiankun框架中,包括微应用的名称、入口、路由等信息。
-
配置微应用生命周期钩子: 可以通过
lifecycles
参数配置微应用的生命周期钩子函数,例如beforeLoad
、beforeMount
、afterMount
等,在不同阶段执行相应的逻辑。 -
配置微应用沙箱环境: Qiankun框架会为每个微应用创建一个独立的沙箱环境,用于隔离微应用之间的全局变量和依赖,
registerMicroApps
方法可以配置这个沙箱环境的相关选项,例如是否启用沙箱、沙箱的沙盒和沙箱间通信等。 -
返回一个销毁函数:
registerMicroApps
方法会返回一个销毁函数,用于在需要移除微应用时执行清理操作,包括清理沙箱环境、取消路由监听等。
综上所述,registerMicroApps
方法主要是用来注册和配置微应用的信息,并为微应用创建一个独立的沙箱环境,以确保微应用之间的隔离和安全性。
4. 独立的沙箱环境实现
Qiankun框架并没有使用iframe
来实现微前端的沙箱化,Qiankun框架实际上是通过基于Proxy
来实现微应用的隔离。
具体来说,Qiankun框架会使用Proxy
对象来包装微应用的全局对象,例如window
、document
等,以及一些特定的API,如setTimeout
、setInterval
等。这样,当微应用尝试访问全局对象或调用特定API时,Qiankun框架会拦截这些操作,并根据沙箱配置来进行处理,以确保微应用之间的隔离。
通过Proxy
对象,Qiankun框架可以实现对微应用的全局变量、DOM操作、定时器等行为进行拦截和控制,从而实现微应用之间的隔离。这种基于Proxy
的沙箱机制相比使用iframe
具有更轻量级的实现,能够更好地保持微前端应用的性能和体验。
// 使用 Proxy 对象创建沙箱环境
const sandbox = new Proxy({}, {
get(target, key) {
// 在这里处理对全局变量的读取操作
console.log(`Reading global variable: ${key}`);
// 返回对应的值
return window[key];
},
set(target, key, value) {
// 在这里处理对全局变量的写入操作
console.log(`Writing global variable: ${key} = ${value}`);
// 更新全局变量的值
window[key] = value;
// 返回是否设置成功
return true;
},
});
2.4 通信机制
Qiankun 使用了自定义事件的方式来进行子应用之间的通信,通过 window 对象上的 CustomEvent API 来实现。
当使用qiankun时,子应用之间可以通过自定义事件来进行通信,qiankun内部使用了浏览器的 CustomEvent API 来实现这一功能。通过自定义事件,子应用可以向其他子应用或主应用发送消息,实现跨应用的数据传递和状态管理。
1. 具体使用示例
以下是一个简单的示例,展示了如何在子应用中使用自定义事件进行通信:
// 发送消息
const event = new CustomEvent('message', {
detail: {
data: 'Hello from sub-app',
},
});
window.dispatchEvent(event);
// 接收消息
window.addEventListener('message', function(event) {
console.log('Received message:', event.detail.data);
});
在这个示例中,子应用通过创建一个自定义事件并调用 dispatchEvent
方法来发送消息,其他子应用或主应用可以通过监听 message
事件并调用 addEventListener
方法来接收消息。
2. 底层实现原理
qiankun内部通过浏览器的 CustomEvent API 来实现自定义事件的功能。CustomEvent 是浏览器提供的一个原生 API,用于创建自定义事件对象。qiankun在子应用中通过创建 CustomEvent 对象,并调用 dispatchEvent
方法来触发事件,从而实现消息的发送。
在接收方,qiankun通过调用 addEventListener
方法来监听特定的自定义事件,一旦事件被触发,就会执行对应的事件处理函数,从而实现消息的接收和处理。
通过这种方式,qiankun实现了子应用之间的通信功能,使得不同子应用之间可以实现数据的传递和共享,从而更好地完成复杂的前端应用场景。