- 作者:老汪软件技巧
- 发表时间:2024-09-28 04:01
- 浏览量:
前言
在上一篇文章中,我们深入探讨了阿里基于 single-spa 的微前端框架 qiankun 及其核心理念和应用。然而,微前端的世界不断发展,京东的 micro-app 作为另一种解决方案,正以其基于 web-component 的设计和强大功能赢得开发者的青睐。micro-app 不仅实现了高效的前端模块化开发,还具备灵活的集成和独立部署能力,大大提升了开发和维护效率。
在本文中,我们将重点介绍 micro-app 的核心概念、关键特性及实际应用场景,帮助你更好地理解和选择这一微前端框架。希望本文能为你的前端开发提供新的视角和有价值的参考。
single-spa 通过监听 URL 变化事件,在路由变化时匹配并渲染相应的子应用。这一思路目前是实现微前端的主流方式。然而,single-spa 要求子应用修改渲染逻辑并暴露三个方法:bootstrap、mount 和 unmount,分别对应初始化、渲染和卸载。这就意味着子应用需要对其入口文件进行修改。此外,使用 qiankun 由于其基于 single-spa 进行封装,也继承了这些特点,并需要对 webpack 配置进行一定的调整。
与此不同,micro-app 并未沿袭 single-spa 的思路,而是借鉴了 WebComponent 的理念。通过将 CustomElement 与自定义的 Shadow DOM 结合,micro-app 将微前端封装成一个类 WebComponent 组件,从而实现组件化渲染。得益于自定义 Shadow DOM 的隔离特性,micro-app 不需要像 single-spa 和 qiankun 那样要求子应用修改渲染逻辑并暴露方法,也无需修改 webpack 配置。这使得 micro-app 成为接入微前端成本最低的方案。
micro-app 和 qiankun 的核心特性对比特性micro-appqiankun
使用简单
类 WebComponent 组件,一行代码嵌入,提供完整功能集。
基于 single-spa,配置简单,提供 js 沙箱、样式隔离等功能。
零依赖
无依赖,体积小,扩展性高。
依赖 single-spa,功能丰富,但体积较大。
兼容性
兼容所有框架,支持独立开发和部署。
与 single-spa 深度绑定,需额外配置支持某些框架。
社区支持与文档
文档详细,社区支持较弱。
社区活跃,文档完善,应用案例多。
学习曲线
简单易用,学习成本低。
需要一定学习曲线,特别是对 single-spa 不熟悉的开发者。
安装与快速上手react 基座应用
1、首先,使用 create-react-app 创建一个新的 React 应用。
npx create-react-app main-app
2、安装micro-app依赖
npm i @micro-zoe/micro-app --save
3、在入口处引入
// index.js
import microApp from '@micro-zoe/micro-app'
microApp.start()
4、React Router 来搭建基座应用的路由系统
// app.js
import React from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import Home from './pages/home';
import About from './pages/about';
import styles from './App.css';
function App() {
return (
<BrowserRouter>
<div style={{ textAlign: 'center', marginTop: '20%' }}>
<header className={styles.header}>
<Link to="/">基座 HomeLink>
<Link to="/about">基座 AboutLink>
header>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
Routes>
div>
);
}
export default App;
效果如图:
4、使用 micro-app 渲染子 react 微应用在主应用的组件文件home.js中配置 micro-app,以便嵌入子应用。
import React from 'react';
const Home = () => {
return (
<div>
<h2>首页Home Pageh2>
<micro-app
name="subapp1"
url="http://localhost:3001/"
baseroute="/"
>micro-app>
div>
);
};
export default Home;
react 子应用
1、 创建子应用
假设已有一个子应用,子应用可以使用任何前端框架创建,但为了演示,假设我们也用 create-react-app 创建子应用。
bash复制代码
npx create-react-app subapp1
cd subapp1
2、配置子应用
在子应用的 package.json 中添加如下配置,以便它能正确地作为 micro-app 的子应用运行。
json复制代码
{
"name": "subapp1",
"version": "0.1.0",
"private": true,
"homepage": ".",
"scripts": {
"start": "set PORT=3001 && react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
// 其它配置项保持不变
}
运行子应用:
bash复制代码
npm start
效果如图:
侵入总结
接入过程非常简单,侵入性操作总结如下:
主应用
启动 micro-app:
javascript复制代码
microApp.start();
添加微应用容器组件:
jsx复制代码
name="subapp1" url="http://localhost:3001/" baseroute="/">
添加路由指向容器组件:
jsx复制代码
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
function App() {
return (
<BrowserRouter>
<div>
<header>
<Link to="/">基座 HomeLink>
<Link to="/about">基座 AboutLink>
header>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
Routes>
div>
BrowserRouter>
);
}
export default App;
微应用
修改 public-path: 在微应用的构建配置中设置正确的 publicPath,确保资源路径正确。例如,在 webpack.config.js 中:
javascript复制代码
output: {
publicPath: './',
},
添加跨域访问: 确保微应用支持跨域访问。在微应用的服务器配置中允许跨域请求。例如:
javascript复制代码
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
next();
});
自动切换路由的 basename: 根据环境自动设置路由的 basename,确保路由切换正确。例如:
javascript复制代码
import { BrowserRouter } from 'react-router-dom';
const basename = window.__MICRO_APP_BASE_ROUTE__ || '/';
<BrowserRouter basename={basename}>
{/* 其他路由配置 */}
BrowserRouter>
通过以上步骤,你可以轻松将 micro-app 集成到主应用中,实现微前端架构的高效开发和部署。
vue 子应用
创建一个新的 vue 应用
使用 vue-cli 创建一个新的 Vue 项目:
bash复制代码
vue create my-vue-app
在项目创建过程中,选择 Vue 3 版本。
修改 public-path: 在 Vue 项目的 vue.config.js 中设置正确的 publicPath,以确保资源路径正确:
javascript复制代码
module.exports = {
publicPath: './',
};
添加跨域访问: 确保微应用支持跨域访问。在开发服务器的配置中允许跨域请求。在 vue.config.js 中添加以下配置:
javascript复制代码
module.exports = {
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},
};
自动切换路由的 basename: 在微应用中根据环境自动设置路由的 basename,确保路由切换正确。在 Vue 项目的 src/main.js 中进行如下配置:
javascript复制代码
import { createApp } from 'vue';
import App from './App.vue';
import { createRouter, createWebHistory } from 'vue-router';
import routes from './routes';
const router = createRouter({
history: createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || '/'),
routes,
});
const app = createApp(App);
app.use(router);
app.mount('#app');
如图所示:
注意:
1、name 必须以字母开头,且不可以带有除中划线和下划线外的特殊符号
2、url 只是 html 地址,子应用的页面渲染还是基于浏览器地址的,关于这点请查看
3、baseroute 的作用请查看
4、子应用必须支持跨域访问,跨域配置参考
数据通信
数据通信是 micro-app 的一大亮点,相较于 qiankun 的 EventBus,micro-app 提供了一种稍微简便一些的使用方式。尽管如此,使用方式仍可能显得有些 Hacky。
父传子
在基座应用的容器组件里,你可以通过 microApp.setData 方法来实现父应用向子应用的数据传递。以下是具体的使用示例:
// 父应用
<template>
<div>
<h1>基座应用h1>
<micro-app name="my-vue-app" url="http://localhost:8081/" @mounted="handleMounted" />
div>
template>
<script>
import microApp from '@micro-zoe/micro-app';
export default {
name: 'Container',
methods: { handleMounted() {
// 在子应用挂载后传递数据
microApp.setData('my-vue-app', { message: 'Hello from the parent app!' }); }, }, };
script>
<style> /* 样式可以根据需要自定义 */ style>
// 子应用
<template>
<div>
<h1>子应用h1>
<p>{{ message }}p>
div>
template>
<script>
export default {
name: 'ChildComponent',
data() {
return {
message: 'No message yet',
};
},
mounted() {
// 监听来自父应用的数据变化
this.$microApp.addDataListener((data) => { if (data && data.message) { this.message = data.message; } }); }, };
script>
<style> /* 样式可以根据需要自定义 */ style>
使用 microApp.setData 可以方便地在基座应用和子应用之间传递数据。这种方式相比于 qiankun 的 EventBus 更加简便,但仍可能显得有些 Hacky。通过上述示例,你可以轻松实现父应用向子应用的数据通信。
子传父
子应用向父应用传递数据在 micro-app 中同样是一个重要的功能。micro-app 提供了 microApp.dispatch 和 microApp.addGlobalDataListener 方法来实现子传父的功能
Micro-App 高级功能
上面介绍了 micro-app 的一些基础操作,它还提供了一些高级功能来增强微前端项目的灵活性和可维护性。
Keep-Alive
保持微应用的状态 Keep-Alive:
javascript复制代码
name='xx' url='xx' keep-alive>
生命周期
你可以通过以下方法来监听微应用的生命周期事件:
javascript复制代码
/** @jsxRuntime classic */
/** @jsx jsxCustomEvent */
import jsxCustomEvent from '@micro-zoe/micro-app/polyfill/jsx-custom-event'
const App = () => {
return (
name='xx'
url='xx'
onCreated={() => console.log('micro-app元素被创建')}
onBeforemount={() => console.log('即将被渲染,只在初始化时执行一次')}
onMounted={() => console.log('已经渲染完成,只在初始化时执行一次')}
onAfterhidden={() => console.log('已卸载')}
onBeforeshow={() => console.log('即将重新渲染,初始化时不执行')}
onAftershow={() => console.log('已经重新渲染,初始化时不执行')}
onError={() => console.log('渲染出错')}
/>
)
}
应用之间跳转隔离JS 隔离
使用 Proxy 拦截用户的全局操作行为,防止对 window 的访问和修改,以避免全局变量污染。
CSS 隔离
提供两种隔离方式:
默认添加 CSS 选择器前缀。ShadowDOM。元素隔离
micro-app 模拟实现了类似 ShadowDOM 的功能,确保元素不会逃离 元素边界,子应用只能对自身的元素进行操作。
静态资源处理使用 globalAssets 共享资源:
javascript复制代码
// index.js
import microApp from '@micro-zoe/micro-app'
microApp.start({
globalAssets: {
js: ['js地址1', 'js地址2', ...], // js地址
css: ['css地址1', 'css地址2', ...], // css地址
}
})
或者使用 global 属性:
html复制代码
rel="stylesheet" href="xx.css" global>
对资源进行过滤:
html复制代码
<link rel="stylesheet" href="xx.css" exclude>
<script src="xx.js" exclude>script>
<style exclude>style>
渲染微前端模式插件系统
插件系统的主要作用是对 JS 进行修改,每一个 JS 文件都会经过插件系统,你可以对这些 JS 进行拦截和处理。插件系统通常用于修复 JS 中的错误或向子应用注入一些全局变量。
这个插件系统主要在中间层处理 JS,避免一些由于固定模板而无法处理的 JS 报错。总的来说,这个系统还在发展中,需要更多开发者一起共建。
项目架构:
常见问题1、子应用一定要支持跨域吗?
是的!
如果是开发环境,可以在 webpack-dev-server 中设置 headers 支持跨域。
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},复制代码Error复制成功
如果是线上环境,可以通过配置 nginx支持跨域。
2、兼容性如何
micro-app 依赖于 CustomElements 和 Proxy 两个较新的 API。
对于不支持 CustomElements 的浏览器,可以通过引入 polyfill 进行兼容,详情可参考:webcomponents/polyfills。
但是 Proxy 暂时没有做兼容,所以对于不支持 Proxy 的浏览器无法运行 micro-app。