在实习的这段时间接触了很多新东西,比如微前端就是其中之一,在这里小浪就来聊聊微前端中的qiankun框架
当我们开发大型的前端应用时,通常需要将应用拆分成多个子应用进行开发和维护。这时候微前端就显得尤为重要。微前端是一种架构模式,它将前端应用程序拆分成多个更小的、相对独立的部分,每个部分都可以独立开发、测试、部署和扩展。在微前端架构中,每个子应用都是一个独立的应用程序,可以独立部署和运行。这样,我们就可以将前端应用程序的开发、测试、部署和维护工作分解成多个更小的任务,提高应用程序的可维护性和可扩展性。微前端的实现方式有多种,其中比较常见的方式是通过 iframe、Web Components 或者 JavaScript 模块加载器来实现子应用的隔离和独立运行。通过这些方式,不同的子应用之间可以互相独立、互相通信,从而实现一个完整的前端应用程序。
所以呢,小浪觉得如果项目具有上面这些特点,那么可以考虑使用微前端来优化项目架构和提升开发效率。
qiankun 官网地址是:qiankun.umijs.org/
官网的例子和教程都很详细,更新速度也快,大家想具体使用的可以去官网查阅
qiankun 是一个基于 Single-SPA 的微前端解决方案,它提供了一种轻量级的前端架构,可以将多个子应用整合成一个整体应用。qiankun 的架构包含两个主要的角色:
qiankun 的主要特点包括:
在使用 qiankun 构建微前端应用时,我们需要按照以下步骤进行:
除此之外,qiankun 还提供了多种高级特性,例如异步加载、公共依赖、样式隔离、缓存优化等,可以进一步提高应用程序的性能和可维护性。
这里举一个demo例子,大家如果感兴趣可以按照小浪这样来进行搭建 一个简单的 qiankun 实例demo
首先需要创建主应用,我们使用 create-react-app 快速创建一个 React 应用:
npx create-react-app qiankun-example
安装依赖
cd qiankun-example
npm install qiankun
修改主应用入口文件 src/index.js,加入以下代码:
import React from 'react';
import Reactdom from 'react-dom';
import { registerMicroApps, start } from 'qiankun';
// 定义子应用列表
const apps = [
{
name: 'vue3',
entry: '//localhost:8081',
container: '#subapp-viewport',
activeRule: '/vue3',
},
{
name: 'react-app',
entry: '//localhost:8082',
container: '#subapp-viewport',
activeRule: '/react-app',
},
];
// 注册子应用
registerMicroApps(apps);
// 启动 qiankun
start();
在上面小浪首先定义了两个子应用,分别是 Vue 3 应用和 React 应用。然后通过 registerMicroApps 函数注册子应用,启动 qiankun。
在主应用中我们定义了一个名为 vue3 的子应用,需要先创建子应用的代码。在 vue-cli 中创建一个 Vue 3 应用(大家也可以用vite搭建,这里我用vue-cli):
vue create subapp-vue3
安装依赖:
cd subapp-vue3
npm install
修改 src/main.js,加入以下代码:
import { createApp } from 'vue';
import App from './App.vue';
let instance = null;
function render() {
instance = createApp(App).mount('#app');
}
// 开发环境下直接渲染
if (process.env.NODE_ENV === 'development') {
render();
}
// 导出渲染函数,qiankun 会调用此函数渲染子应用
export async function bootstrap() {}
export async function mount() {
render();
}
export async function unmount() {
instance.$destroy();
}
上面小浪定义了子应用的渲染函数,用于在主应用中渲染子应用。在开发环境下直接渲染,而在生产环境下则通过导出 bootstrap、mount 和 unmount 函数,由 qiankun 调用。
我们需要先创建 React 应用:
npx create-react-app subapp-react
安装依赖:
cd subapp-react
npm install
修改 src/index.js,加入以下代码:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
let instance = null;
function render() {
ReactDOM.render(<App />, document.getElementById('root'));
}
// 开发环境下直接渲染
if (process.env.NODE_ENV === 'development') {
render();
}
// 导出渲染函数,qiankun 会调用此函数渲染子应用
export async function bootstrap() {}
export async function mount() {
render();
}
export async function unmount() {
instance.$destroy();
}
这里同样定义了子应用的渲染函数,用于在主应用中渲染子应用。在开发环境下直接渲染,而在生产环境下则通过导出 bootstrap、mount 和 unmount 函数,由 qiankun 调用。
我们需要分别启动主应用和两个子应用
# 启动主应用
cd qiankun-example
npm start
# 启动 Vue 3 子应用
cd subapp-vue3
npm run serve -- --port 8081
# 启动 React 子应用
cd subapp-react
npm start -- --port 8082
然后在浏览器中访问 **http://localhost:3000**,即可看到主应用的页面。在主应用中点击“Vue 3”或“React App”按钮,即可跳转到对应的子应用页面。在子应用页面中修改内容,可以看到主应用中的内容也随之变化。
注意:在子应用中需要引入 qiankun 库,在 Vue 3 应用中需要安装 qiankun,如下:
npm install qiankun
// Vue 3 子应用中需要引入 qiankun
import { createApp } from 'vue';
import { registerMicroApps, start } from 'qiankun';
import App from './App.vue';
createApp(App).mount('#app');
// 注册子应用
registerMicroApps([...]);
// 启动 qiankun
start();
React子应用
// React 子应用中需要引入 qiankun
import React from 'react';
import ReactDOM from 'react-dom';
import { registerMicroApps, start } from 'qiankun';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
// 注册子应用
registerMicroApps([...]);
// 启动 qiankun
start();
registerMicroApps([...]) 中的 [...] 是一个数组,其中包含了注册的子应用的配置信息,每个配置项是一个对象,至少包含以下几个属性:
例如
registerMicroApps([
{
name: 'vue3',
entry: '//localhost:8081',
container: '#vue3',
activeRule: '/vue3',
},
{
name: 'react',
entry: '//localhost:8082',
container: '#react',
activeRule: '/react',
},
]);
以上代码注册了两个子应用 一个是 Vue 3 应用,一个是 React 应用。
注意
1.entry 属性的值应该是子应用的访问 URL,可以是本地开发服务器的地址,也可以是部署到服务器上的地址。container 属性的值可以是一个 DOM 元素,也可以是一个选择器字符串。在子应用中,渲染组件时需要将组件挂载到该容器中。activeRule 属性的值是一个路径匹配规则,表示子应用应该在哪些 URL 下被激活,可以是字符串或正则表达式。如果当前 URL 匹配了该规则,则该子应用会被激活。
2.子应用中也需要写 registerMicroApps([...]) 来注册子应用。不同之处在于,子应用注册时需要指定一些配置项,以便于在主应用中正确加载和管理子应用。 在子应用中,通常会将 registerMicroApps([...]) 封装成一个函数,以便于主应用加载子应用时调用该函数注册子应用。例如:
import { registerMicroApps, start } from 'qiankun';
function render() {
// 渲染子应用
}
function initApp() {
// 初始化子应用
}
// 注册子应用
function register() {
registerMicroApps([
{
name: 'vue3',
entry: '//localhost:8081',
container: '#vue3',
activeRule: '/vue3',
},
]);
start();
}
// 启动子应用
function bootstrap() {
initApp();
render();
}
// 如果是独立运行,直接启动子应用
if (!window.__POWERED_BY_QIANKUN__) {
bootstrap();
}
// 如果是作为子应用加载,注册子应用
export async function mount(props) {
bootstrap();
}
// 如果是作为子应用卸载,销毁子应用
export async function unmount(props) {
// 销毁子应用
}
在上面的代码中,register() 函数用来注册子应用,其中的配置项与主应用中的相同。bootstrap() 函数用来启动子应用,其中包含了初始化和渲染等过程。mount() 函数用来作为子应用加载时的入口,其中包含了注册、初始化、渲染等过程。unmount() 函数用来作为子应用卸载时的入口,其中包含了销毁子应用的过程。需要注意的是,如果子应用是独立运行的,即非通过主应用加载的,那么直接调用 bootstrap() 启动子应用即可。
主应用可以通过 setGlobalState 方法来设置全局状态,子应用可以通过 onGlobalStateChange 方法来监听全局状态的变化。这样,当主应用修改了全局状态时,所有的子应用都可以得到通知并进行相应的处理。
在主应用中设置全局状态:
import { setGlobalState } from 'qiankun';
setGlobalState({ user: { name: 'lang', age: 18 } });
在子应用中监听全局状态的变化:
import { onGlobalStateChange, setGlobalState } from 'qiankun';
onGlobalStateChange((newState, prevousState) => {
console.log('newState:', newState);
console.log('prevousState:', prevousState);
});
// 修改全局状态
setGlobalState({ user: { name: 'xiaolang', age: 20 } });
子应用之间可以通过主应用作为中介来实现状态的共享。具体实现方式如下:
在主应用中设置共享状态
import { setGlobalState } from 'qiankun';
setGlobalState({ user: { name: 'lang', age: 18 } });
在主应用中将共享状态传递给子应用:
import { start } from 'qiankun';
const app = start({
name: 'vue3',
entry: '//localhost:8081',
container: '#vue3',
activeRule: '/vue3',
props: {
shared: {
getGlobalState: () => Promise.resolve(globalState),
setGlobalState: (state) => Promise.resolve(setGlobalState(state)),
}
}
});
在子应用中使用共享状态:
import { initGlobalState } from 'qiankun';
const actions = initGlobalState({ user: { name: 'lang1', age: 19 } });
actions.onGlobalStateChange((newState, prevousState) => {
console.log('newState:', newState);
console.log('prevousState:', prevousState);
});
// 修改共享状态
actions.setGlobalState({ user: { name: 'lang2', age: 20 } });
上面,主应用通过 props 将 getGlobalState 和 setGlobalState 传递给了子应用。子应用可以通过 initGlobalState 方法来初始化全局状态,并且可以监听全局状态的变化。在子应用中,如果需要修改全局状态,可以通过 setGlobalState 方法来实现。
可以先在主应用中通过 registerMicroApps 方法注册好所有的子应用,并在主应用中管理子应用之间的通信。然后,可以在需要使用其他子应用组件的子应用中通过 loadMicroApp 方法异步加载对应的子应用并获取到对应子应用的实例,从而使用其提供的组件。
import { loadMicroApp } from 'qiankun';
const loadReactApp = () => loadMicroApp({
name: 'reactApp',
entry: '//localhost:8082',
container: '#react',
activeRule: '/react',
});
// 在需要使用 ReactApp 中的组件的 VueApp 组件中异步加载 ReactApp,并使用其提供的组件
const VueApp = {
template: `
<div>
<h1>VueApp</h1>
<react-component />
</div>
`,
mounted() {
loadReactApp().then(app => {
const ReactComponent = app.getComponent('ReactComponent');
Vue.component('react-component', {
template: `<div><ReactComponent /></div>`,
});
});
},
};
在 VueApp 的 mounted 生命周期中异步加载了 ReactApp 并获取到其实例,然后通过 getComponent 方法获取到了 ReactApp 中提供的 ReactComponent 组件,并将其转换为 Vue 组件供 VueApp 使用。
React 子应用中,加载 Vue 子应用并获取其组件:
import { loadMicroApp, getGlobalState } from 'qiankun';
function App() {
const [vueComponent, setVueComponent] = useState(null);
useEffect(() => {
const vueApp = loadMicroApp({
name: 'vue3',
entry: '//localhost:8081',
container: '#vue3',
activeRule: '/vue3',
props: { name: 'vue-app' }
});
vueApp.onGlobalStateChange((state, prev) => {
console.log('[React] Vue global state changed: ', state);
});
vueApp
.loadPromise.then(() => {
const vueInstance = vueApp.bootstrap();
setVueComponent(vueInstance.$children[0]);
});
return () => vueApp.unmount();
}, []);
return (
<div>
<h1>React App</h1>
<hr />
<h2>Vue App Component:</h2>
{vueComponent && (
<div>
<p>{vueComponent.message}</p>
<button onClick={() => vueComponent.handleClick()}>Click me!</button>
</div>
)}
</div>
);
}
export default App;
当然得在 Vue 子应用中,暴露一个组件,上面的vueInstance.$children[0]就是这个组件:
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello from Vue!'
};
},
methods: {
handleClick() {
console.log('Vue button clicked!');
}
}
};
</script>
在调用其他子应用的组件时,可以使用 props 传递参数,和在普通的 React 组件中传递 props 是类似的。具体来说,在调用时,可以将需要传递的参数放在 props 对象中,然后作为第二个参数传递给 render 方法,例如:
import React from 'react';
import { render, hydrate } from 'react-dom';
import { registerMicroApps, start } from 'qiankun';
import App from './App';
// 注册子应用
registerMicroApps([...]);
// 启动 qiankun
start();
// 在 App 组件中调用其他子应用的组件
function App() {
return (
<div>
{/* 调用 Vue 子应用的组件 */}
<div>
{render('vue3', { name: 'Tom' }, { container: '#vue-container' })}
</div>
</div>
);
}
// 渲染根组件
hydrate(<App />, document.getElementById('root'));
我们向 Vue 子应用的组件中传递了一个名为 name 的参数,并且将其渲染到了 #vue-container 容器中。在 Vue 子应用中,可以通过 props 对象获取这个参数,例如:
<template>
<div>
<h2>Vue Component</h2>
<p>Hello, {{ name }}!</p>
</div>
</template>
<script>
export default {
name: 'VueComponent',
props: {
name: {
type: String,
required: true,
},
},
};
</script>
这样,在 Vue 子应用中就可以通过 this.$props.name 访问这个参数了
由于 Vue 和 React 使用了不同的渲染引擎,它们之间的组件并不完全兼容。我这个也是自己想尝试一下不同的语言他们互相调用,所以大家在实际开发中需要根据具体情况来选择使用哪种方式来实现组件之间的互相调用。一般的话还是vue调用vue, react调用react兼容比较好。
上面就是就是小浪介绍的一个简单的 qiankun 实例demo,包括主应用使用 React,子应用1 使用 Vue 3,子应用2 使用 React 。
在平时的开发肯定也不是一帆风顺的,也会多多少少遇到一些问题,下面是我平时遇到的一些问题,也查阅一些解决措施,这里就不一一详细把解决措施写出来,哈哈哈,写不完。
由于主应用和子应用都需要进行路由处理,需要注意路由的定义、匹配和传递,确保各个子应用之间的路由能够正确地被处理。
措施:统一路由管理:尽量采用相同的路由管理方式,确保路由的定义和匹配方式相同,方便进行传递和处理。
由于不同的子应用之间可能会有样式冲突的问题,需要考虑如何进行样式隔离,以确保各个子应用之间的样式不会互相干扰。
措施:可以采用 css Modules、BEM 等方式进行样式隔离,或者使用 Shadow DOM 等技术进行样式隔离。在公司里后面全部改用CSS Modules。
由于各个子应用之间需要共享一些全局状态,需要考虑如何进行全局状态管理,以便于各个子应用之间能够共享数据。
措施:可以采用 Redux、Mobx 等全局状态管理库进行状态管理,或者使用 qiankun 提供的 props、emit 等方式进行状态传递。
由于 qiankun 微前端框架会对主应用和子应用进行生命周期管理,因此需要注意各个组件的生命周期方法的调用时机和顺序。
措施:可以在各个生命周期方法中进行必要的处理,或者使用 qiankun 提供的 emitLifeCycles 等方式进行生命周期管理。
由于 qiankun 微前端框架会对各个子应用进行缓存,需要注意缓存的清理和更新,以确保各个子应用之间的数据能够及时地同步更新
措施:可以使用 qiankun 提供的 prefetch、sandbox 等方式进行缓存管理,或者手动清理和更新缓存。
在多个子应用之间共享组件时,应该避免重复引入公共依赖。一种解决方法是将公共依赖打包为一个单独的库,并将其发布到npm上,然后在子应用中通过npm依赖引入。另一种解决方法是将公共依赖打包为umd格式,并在qiankun的global对象上注册,使得其他子应用可以通过global对象来访问公共依赖。例如:
在主应用中注册公共依赖:
import React from 'react';
import ReactDOM from 'react-dom';
import MyComponent from 'my-component';
window.MyComponent = MyComponent;
在子应用中使用公共依赖:
import React from 'react';
import ReactDOM from 'react-dom';
import { useEffect } from 'react';
const { MyComponent } = window;
这可能是我自己会遇到的问题,有时候因为自己拼错单词,导致没有注册成功.....应用注册是一个关键步骤,需要确保主应用和子应用都能够正确地注册到qiankun微前端框架中。在主应用中,应该正确地调用registerMicroApps函数,并确保子应用的entry和container属性都被正确地设置。在子应用中,应该正确地调用start函数,并确保子应用的mount函数能够正确地渲染子应用。
有的时候查找微前端相关的问题的时候,下面总有言论,不看好微前端
确实,微前端需要解决许多复杂的问题,例如应用程序的拆分、应用程序之间的通信和数据共享等。这会增加开发和维护的复杂性,可能需要额外的学习成本和工具支持。微前端应用程序可能需要通过网络加载许多小块代码,这可能会影响应用程序的性能和用户体验。此外,微前端还需要在运行时处理一些额外的逻辑,例如路由和状态管理,微前端应用程序可能需要跨域通信和数据共享,这可能会引入一些安全风险。此外,微前端还需要确保应用程序的隔离和沙箱,以防止应用程序之间的影响和攻击。技术选型:微前端需要处理不同的技术栈和框架之间的兼容性和交互性。这可能需要额外的工作。等等问题。
虽然微前端存在一些挑战和限制,但随着技术的发展和社区的支持,这些问题将得到解决,微前端也将成为构建大型前端应用程序的一种有力选择。总之,qiankun 是一个非常优秀的微前端解决方案,它可以帮助我们更好地管理和维护前端应用程序,提高应用程序的可扩展性和可维护性。同时,qiankun 也是一个非常灵活和可扩展的框架,可以适用于各种前端应用程序的开发场景。
微前端架构是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。由此带来的变化是,这些前端应用可以独立运行、独立开发、独立部署。
在 Web 开发导论/微前端与大前端一文中,笔者简述了微服务与微前端的设计理念以及微前端的潜在可行方案。微服务与微前端,都是希望将某个单一的单体应用,转化为多个可以独立运行、独立开发、独立部署、独立维护的服务或者应用的聚合
“微前端架构”是一种使用微服务模式构建前端应用的方法。微前端的理念是将你的前端拆分为一组可独立部署、松散耦合的应用。然后将这些应用组装在一起以创建面向用户的单个应用程序
在过去的几星期里,随着 Martin Fowler 博客上,那篇 Cam Jackson 写的微前端的文章发布,到处都在讨论 Microfrontend。作为一个微前端 “专家”,我也分享一下:如何去落地微前端
微前端架构是一种设计方法,其中,前端应用被分解为多个松散而协同工作的半独立“微应用”。微前端的思想来源于微服务,其名称也遵循了微服务的命名方式。那么,微前端的优势和好处在哪?让我们一起通过这篇微前端教程来了解
就目前来看,微前端已经不是一个新话题了。随着越来越多的公司的深入研究,当前也提出了很多的解决方案。不过本文不是想要来介绍微前端,更想介绍项目如何一步步到达微前端架构的实际需求
核心的就是渲染器,它提供了最基础渲染能力,有了它,你可以实现微前端、微服务、远程组件、首屏渲染,甚至可以和 React、EJS 等配合使用。
微前端开发常见问题汇总,前端应用可以独立运行、独立开发、独立部署。微前端不是单纯的前端框架或者工具而是一套架构体系。其在开发中会有各种问题,今天小编整理了一下分享给大家!
Single SPA 是一个用于前端微服务的 javascript 框架。它使你可以在单页应用中使用多个框架,这样就可以按功能拆分代码,并 能使 Angular、React、Vue.js 程序一起运行
不同于单纯的前端框架/工具,微前端是一套架构体系,这个概念最早在2016年底由 ThoughtWorks 提出。 微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,将 Web 应用从整个的「单体应用」转变为多个小型前端应用的「聚合体」。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!