uniapp与微前端
# uniapp与微前端
# uniapp
# 微前端
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。其实,简单点来说就是:微前端是一种前端架构设计模式,其思想是将一个大型的前端应用拆分成多个独立的小型子应用,每个子应用都可以独立开发、构建、测试和部署,并且可以被组合成一个完整的前端应用。
微前端是一种设计架构,并不是技术。是借鉴于微服务思想的设计架构,是一种为了解决庞大且难以维护的项目的方案。
- 微前端解决了什么问题
- 大型应用程序的维护困难
- 大型应用程序的可扩展性问题
- 多团队协同开发问题
- 微前端具备的核心价值
- 技术栈无关
- 独立开发、独立部署
- 增量升级
- 独立运行时
- 微前端的优点
- 更好的代码可维护性
- 降低了开发的成本以及复杂性
- 更好的可重用性和可扩展性
- 更高的可靠性和安全性
微前端的缺点: 技术复杂度更高, 存在技术风险, 项目总体运行效率可能会变慢
# 微前端解决跨域的方案
- Proxy
通过配置Web服务器反向代理,将多个前端应用程序转发到同一个主机的端口上。需要Nginx、Apache等服务器和一个负载均衡模块,允许所有的请求通过同一个域名和端口号对外访问。
- iframe
我们可以通过
iframe标签来解决主应用与子应用的跨域问题,主应用程序通过iframe标签嵌入子应用,并且使用postMessage来允许不同iframe元素之间进行通信。
- CORS:
"Access-Control-Allow-Origin": "*"
# 微前端架构方案
自由组织模式
基座模式
将多个子应用程序作为模块加载到一个主应用程序中。这些模块是独立的小型应用程序。每个子应用程序都可以独立开发、测试、部署,而主应用程序主要就是将子应用进行集成和协调,子应用程序发生变化,主应用程序也会自动的完成更新。
基座模式需要使用一些基础设施来支持子应用程序加载、路由和通信
Single-SPA (opens new window):一个支持多框架、多技术栈的JavaScript微前端框架,用于构建大型单页应用程序。
qiankun (opens new window):一个基于Single-SPA封装的微前端框架,支持React、Vue、Angular等技术栈。
- 去中心化模式
是在多个子应用程序之间创建对等的关系,每个应用程序对应整个应用程序系统来说都是平等的。
# Single-SPA实践
# 创建主应用
npx create-single-spa
? Directory for new project wei-fe-spa # 项目名称
? Select type to generate single-spa root config # 创建微前端容器应用,根应用就是主应用,负责加载其他子应用,并作为单页应用(SPA)的容器。
? Which package manager do you want to use? pnpm # pnpm
? Will this project use Typescript? Yes # ts
? Would you like to use single-spa Layout Engine No # no
? Organization name (can use letters, numbers, dash or underscore) zhou # 组织名称
# 创建react子应用
cd wei-fe-spa
➜ wei-fe-spa git:(master) ✗ npx create-single-spa
? Directory for new project react-project # 子项目名称
? Select type to generate single-spa application / parcel # 微前端架构中的子应用程序,可以使用Vue、React、Angular框架,可以利用根应用提供的共享工具和服务进行通信。
? Which framework do you want to use? react # 选择框架
? Which package manager do you want to use? pnpm # pnpm
? Will this project use Typescript? Yes # ts
? Organization name (can use letters, numbers, dash or underscore) zhou # 组织名称
? Project name (can use letters, numbers, dash or underscore) react-project # 项目名称
- 注册子应用
// wei-fe-spa/src/zhou-root-config.ts
// 注册子应用
registerApplication({
name: "@zhou/react-project",
app: () => System.import("@zhou/react-project"),
activeWhen: ["/react-project"]
});
- 声明导入配置:
// wei-fe-spa/src/index.ejs
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/system/single-spa.min.js",
{/* 以cdn的形式导入react */}
"react": "https://unpkg.com/react@17/umd/react.production.min.js",
"react-dom": "https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"
}
}
</script>
<% if (isLocal) { %>
<script type="systemjs-importmap">
{
"imports": {
"@zhou/root-config": "//localhost:9000/zhou-root-config.js",
{/* 声明导入路径 */}
"@zhou/react-project": "//localhost:9010/zhou-react-project.js" // 这个路径可以进入到子应用,直接 npm run start 启动子应用,在页面上即可看到~
}
}
</script>
<% } %>
npm run start, 先启动子应用,再启动主应用访问主应用服务:
http://localhost:9000,访问子应用服务:http://localhost:9000/react-project
# 创建vue子应用
➜ wei-fe-spa git:(master) ✗ npx create-single-spa
? Directory for new project vue-demo
? Select type to generate single-spa application / parcel
? Which framework do you want to use? vue
? Organization name (can use letters, numbers, dash or underscore) zhou
注册,配置跟上面的react一样~
# 创建公共模块子应用
跨应用通信
- 创建:
➜ wei-fe-spa git:(master) ✗ npx create-single-spa
? Directory for new project utils
? Select type to generate in-browser utility module (styleguide, api cache,
etc) # 公共模块应用,非渲染组件,可以在不同应用之间共享JavaScript模块和组件。
? Which framework do you want to use? none
? Which package manager do you want to use? pnpm
? Will this project use Typescript? Yes
? Organization name (can use letters, numbers, dash or underscore) zhou
? Project name (can use letters, numbers, dash or underscore) utils
- 导出函数:
// wei-fe-spa/utils/src/zhou-utils.ts
// 导出一个函数
export function addFn(a: number, b: number) {
return a + b
}
- 主应用中导入,跟上面的react一样,然后
npm run start启动项目
这只是一个工具库,无需注册路由~
- vue子应用中使用:
// wei-fe-spa/vue-demo/src/App.vue
mounted() {
System.import('@zhou/utils').then(res => {
console.log('res.addFn(1, 2)', res.addFn(1, 2))
})
}
# qiankun
qiankun是一个基于Single-SPA的微前端解决方案,它可以帮我们将多个独立的前端应用整合到一个整体,并实现这些应用的共享和协同。
特性
- 基于single-spa封装,提供了更加开箱即用的API。
- 与技术栈无关,任意技术栈的应用均可使用/接入,不论是react、vue、angular还是其他框架。
- html entry接入方式,让你接入微应用像使用iframe一样简单。
- 样式隔离,确保微应用之间样式互相不干扰。
- js沙箱,确保微应用之间全局变量/事件不冲突。
- 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加快微应用打开速度。
Q:why not iframe?
如果不考虑体验问题,iframe 几乎是最完美的微前端解决方案了。
iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但他的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题:
- url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
- UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中..
- 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
- 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。
其中有的问题比较好解决(问题1),有的问题我们可以睁一只眼闭一只眼(问题4),但有的问题我们则很难解决(问题3)甚至无法解决(问题2),而这些无法解决的问题恰恰又会给产品带来非常严重的体验问题, 最终导致我们舍弃了 iframe 方案。
Why Not Iframe (opens new window)
# 通信
- 在主应用中进行注册的时候可以传递props参数;在子应用index.js文件中的mount生命周期中获取props参数
// 在主应用中进行注册的时候可以传递props参数:
registerMicroApps([
{
name: "reactApp",
entry: "//localhost:3011",
container: "#micro-app1",
activeRule: "/micro-app1",
props: {
name: "青峰1",
},
},
{
name: "vueApp",
entry: "//localhost:3012",
container: "#micro-app2",
activeRule: "/micro-app2",
props: {
name: "青峰2",
},
},
]);
// 子应用入口文件
export async function mount(props) {
console.log("[react16] props from main framework", props);
// 监听主应用传递的数据
props.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log(state, prev);
});
// 向主应用传递数据
// props.setGlobalState(state);
render(props);
}
# 更改react配置
方案1:暴露webapck配置信息:react-scripts eject
会暴露所有配置信息
方案2:react-app-rewired (opens new window)
此工具可以在不 'eject' 也不创建额外 react-scripts 的情况下修改 create-react-app 内置的 webpack 配置,然后你将拥有 create-react-app 的一切特性,且可以根据你的需要去配置 webpack 的 plugins, loaders 等。
# 总结
微前端是指将前端应用程序拆分成更小的独立部分,然后将其组合成一个整体应用程序。这种架构方式可以使不同团队独立开发、部署和维护各自的功能模块,从而提高应用程序的可维护性、灵活性和可扩展性。
微前端未来的发展趋势将会是更加成熟化和标准化。目前还有一些痛点,例如多语言支持、子应用间通信、路由同步等问题需要解决。因此,微前端框架和相关工具的发展将会更加完善,提供更好的解决方案来解决这些问题。同时,由于微前端的高度灵活性,未来很有可能出现类似于微服务的组织模式,即更多的团队会提供独立的子应用来实现一些业务需求。
# 备注
- 微前端解决的是隔离和通信,monorepo解决的是组件共享
# 参考
服务端发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放、chatgpt聊天消息发送就是这样的