admin 管理员组文章数量: 887032
micro
关于micro-app
在micro-app
之前,业内已经有一些开源的微前端框架,比较流行的有2个:single-spa
和qiankun
。
single-spa
是通过监听url change
事件,在路由变化时匹配到渲染的子应用并进行渲染,这个思路也是目前实现微前端的主流方式。同时single-spa
要求子应用修改渲染逻辑并暴露出三个方法:bootstrap、mount、unmount
,分别对应初始化、渲染和卸载,这也导致子应用需要对入口文件进行修改。因为qiankun
是基于single-spa进行封装
,所以这些特点也被qiankun
继承下来,并且需要对webpack
配置进行一些修改。
micro-app
并没有沿袭single-spa
的思路,而是借鉴了WebComponent
的思想,通过CustomElement
结合自定义的ShadowDom
,将微前端封装成一个类WebComponent
组件,从而实现微前端的组件化渲染。并且由于自定义ShadowDom
的隔离特性,micro-app
不需要像single-spa
和qiankun
一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改webpack
配置,是目前市面上接入微前端成本最低的方案。
概念图
micro-app的优势
1、使用简单
我们将所有功能都封装到一个类WebComponent组件中,从而实现在基座应用中嵌入一行代码即可渲染一个微前端应用。
同时micro-app
还提供了js沙箱
、样式隔离
、元素隔离
、预加载
、数据通信
、静态资源补全
等一系列完善的功能。
2、零依赖
micro-app
没有任何依赖,这赋予它小巧的体积和更高的扩展性。
3、兼容所有框架
为了保证各个业务之间独立开发、独立部署的能力,micro-app
做了诸多兼容,在任何技术框架中都可以正常运行。
以React
搭建
本篇以React 16、17作为案例介绍react的接入方式,其它版本react的接入方式以此类推。我们默认开发者掌握了各版本react的开发技巧,如示例中useEffect,在不支持hooks的版本中转换为componentDidMount。
基座应用
我们强烈建议基座应用采用history模式,hash路由的基座应用只能加载hash路由的子应用,history模式的基座应用对这两种子应用都支持。
在以下案例中,我们默认基座的路由为history模式
。
1、安装依赖
npm i @micro-zoe/micro-app --save
2、在入口处引入
// entry
import microApp from '@micro-zoe/micro-app'microApp.start()
3、分配一个路由给子应用
// router.js
import { BrowserRouter, Switch, Route } from 'react-router-dom'
import MyPage from './my-page'export default function AppRoute () {return (<BrowserRouter><Switch>// 👇 非严格匹配,/my-page/* 都指向 MyPage 页面<Route path='/my-page'><MyPage /></Route></Switch></BrowserRouter>)
}
4、在页面中嵌入子应用
export function MyPage () {return (<div><h1>子应用</h1><micro-appname='app1' // name(必传):应用名称url='http://localhost:3000/' // url(必传):应用地址,会被自动补全为http://localhost:3000/index.htmlbaseroute='/my-page' // baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`></micro-app></div>)
}
子应用
1、设置跨域支持
使用create-react-app
脚手架创建的项目,在 config/webpackDevServer.config.js
文件中添加headers
。
其它项目在webpack-dev-server
中添加headers
。
headers: {'Access-Control-Allow-Origin': '*',
}
2、设置基础路由(如果基座是history路由,子应用是hash路由,这一步可以省略)
// router.js
import { BrowserRouter, Switch, Route } from 'react-router-dom'export default function AppRoute () {return (// 👇 设置基础路由,如果没有设置baseroute属性,则window.__MICRO_APP_BASE_ROUTE__为空字符串<BrowserRouter basename={window.__MICRO_APP_BASE_ROUTE__ || '/'}>...</BrowserRouter>)
}
3、设置 publicPath
这一步借助了webpack的功能,避免子应用的静态资源使用相对地址时加载失败的情况,详情参考webpack文档 publicPath
如果子应用不是webpack
构建的,这一步可以省略。
步骤1: 在子应用src目录下创建名称为public-path.js
的文件,并添加如下内容
// __MICRO_APP_ENVIRONMENT__和__MICRO_APP_PUBLIC_PATH__是由micro-app注入的全局变量
if (window.__MICRO_APP_ENVIRONMENT__) {// eslint-disable-next-line__webpack_public_path__ = window.__MICRO_APP_PUBLIC_PATH__
}
步骤2: 在子应用入口文件的最顶部引入public-path.js
// entry
import './public-path'
4、监听卸载
子应用被卸载时会接受到一个名为unmount
的事件,在此可以进行卸载相关操作。
window.addEventListener('unmount', function () {ReactDOM.unmountComponentAtNode(document.getElementById('root'))
})
实战案例
以上介绍了react如何接入微前端,但在实际使用中会涉及更多功能,如数据通信、路由跳转、打包部署,为此我们提供了一套案例,用于展示react作为基座嵌入(或作为子应用被嵌入) react、vue、angular、vite、nextjs、nuxtjs等框架,在案例中我们使用尽可能少的代码实现尽可能多的功能。
案例地址:
常见问题
1、create-react-app创建的子应用,被嵌入微前端后sockjs-node报错
报错信息: WebSocket connection to 'ws://localhost:3000/sockjs-node' failed
原因: 子应用的sockjs-node会根据当前页面的端口号进行通信,嵌入微前端后,端口号为基座的,而非子应用的,导致报错。 虽然这个问题不影响应用的正常运行,但还是要进行处理。
解决方式: 使用插件系统补全子应用sockjs-node的端口号。
microApp.start({plugins: {modules: {子应用名称: [{loader(code) {if (process.env.NODE_ENV === 'development' && code.indexOf('sockjs-node') > -1) {code = code.replace('window.location.port', 子应用端口)}return code}}],}}
})
以Vue
搭建
本篇以Vue 2、3作为案例介绍vue的接入方式,其它版本vue的接入方式以此类推,我们默认开发者掌握了各版本vue的开发技巧,比如示例中vue2的代码如何转换为vue1。
基座应用
我们强烈建议基座应用采用history模式,hash路由的基座应用只能加载hash路由的子应用,history模式的基座应用对这两种子应用都支持。
在以下案例中,我们默认基座的路由为history模式
。
1、安装依赖
npm i @micro-zoe/micro-app --save
2、在入口处引入
// entry
import microApp from '@micro-zoe/micro-app'microApp.start()
3、分配一个路由给子应用
Vue2版本
// router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import MyPage from './my-page.vue'Vue.use(VueRouter)const routes = [{// 👇 非严格匹配,/my-page/* 都指向 MyPage 页面path: '/my-page/*',name: 'my-page',component: MyPage,},
]export default routes
Vue3版本
// router.js
import { createRouter, createWebHistory } from 'vue-router'
import MyPage from './my-page.vue'const routes = [{// 👇 非严格匹配,/my-page/* 都指向 MyPage 页面path: '/my-page/:page*',name: 'my-page',component: MyPage,},
]const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes
})export default router
4、在页面中嵌入子应用
<!-- my-page.vue -->
<template><div><h1>子应用</h1><!-- name(必传):应用名称url(必传):应用地址,会被自动补全为http://localhost:3000/index.htmlbaseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`--><micro-app name='app1' url='http://localhost:3000/' baseroute='/my-page'></micro-app></div>
</template>
子应用
1、设置跨域支持
在vue.config.js
中添加配置
devServer: {headers: {'Access-Control-Allow-Origin': '*',}
}
2、设置基础路由(如果基座是history路由,子应用是hash路由,这一步可以省略)
Vue2版本
// main.js
import VueRouter from 'vue-router'
import routes from './router'const router = new VueRouter({mode: 'history',// 👇 __MICRO_APP_BASE_ROUTE__ 为micro-app传入的基础路由base: window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL,routes,
})
Vue3版本
// main.js
import { createRouter, createWebHistory } from 'vue-router'
import routes from './router'const router = createRouter({// 👇 __MICRO_APP_BASE_ROUTE__ 为micro-app传入的基础路由history: createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL),routes,
})
3、设置 publicPath
这一步借助了webpack的功能,避免子应用的静态资源使用相对地址时加载失败的情况,详情参考webpack文档 publicPath
如果子应用不是webpack构建的,这一步可以省略。
步骤1: 在子应用src目录下创建名称为public-path.js
的文件,并添加如下内容
// __MICRO_APP_ENVIRONMENT__和__MICRO_APP_PUBLIC_PATH__是由micro-app注入的全局变量
if (window.__MICRO_APP_ENVIRONMENT__) {// eslint-disable-next-line__webpack_public_path__ = window.__MICRO_APP_PUBLIC_PATH__
}
步骤2: 在子应用入口文件的最顶部引入public-path.js
// entry
import './public-path'
4、监听卸载
子应用被卸载时会接受到一个名为unmount
的事件,在此可以进行卸载相关操作。
Vue2版本
// main.js
const app = new Vue(...)// 监听卸载操作
window.addEventListener('unmount', function () {app.$destroy()
})
Vue3版本
// main.js
const app = createApp(App)
app.mount('#app')// 监听卸载操作
window.addEventListener('unmount', function () {app.unmount()
})
实战案例
以上介绍了vue如何接入微前端,但在实际使用中会涉及更多功能,如数据通信、路由跳转、打包部署,为此我们提供了一套案例,用于展示vue作为基座嵌入(或作为子应用被嵌入) react、vue、angular、vite、nextjs、nuxtjs等框架,在案例中我们使用尽可能少的代码实现尽可能多的功能。
案例地址:
常见问题
1、基座应用中抛出警告,micro-app未定义
报错信息:
vue2: [Vue warn]: Unknown custom element: <micro-app>
vue3: [Vue warn]: Failed to resolve component: micro-app
参考issue: vue-next@1414
解决方式: 在基座应用中添加如下配置
Vue2版本
在入口文件main.js中设置ignoredElements,详情查看:
// main.js
import Vue from 'vue'Vue.config.ignoredElements = ['micro-app',
]
Vue3版本
在vue.config.js中添加chainWebpack配置,如下:
// vue.config.js
module.exports = {chainWebpack: config => {config.module.rule('vue').use('vue-loader').tap(options => {options.compilerOptions = {...(options.compilerOptions || {}),isCustomElement: (tag) => /^micro-app/.test(tag),};return options})}
}
Vite + Vue3版本
在vite.config.js中通过vue插件设置isCustomElement,如下:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'export default defineConfig({plugins: [vue({template: {compilerOptions: {isCustomElement: tag => /^micro-app/.test(tag)}}})],
})
2、当基座和子应用都是vue-router4,点击浏览器返回按钮页面丢失
原因: vue-router4
没有对路由堆栈state做唯一性标记,导致基座和子应用相互影响,vue-router3及其它框架路由没有类似问题。
测试版本: vue-router@4.0.12
相关issue: 155
解决方式: 在子应用中添加如下设置
if (window.__MICRO_APP_ENVIRONMENT__) {// 如果__MICRO_APP_BASE_ROUTE__为 `/基座应用基础路由/子应用基础路由/`,则应去掉`/基座应用基础路由`// 如果对这句话不理解,可以参考案例: realBaseRoute = window.__MICRO_APP_BASE_ROUTE__router.beforeEach(() => {if (typeof window.history.state?.current === 'string') {window.history.state.current = window.history.state.current.replace(new RegExp(realBaseRoute, 'g'), '')}})router.afterEach(() => {if (typeof window.history.state === 'object') {window.history.state.current = realBaseRoute + (window.history.state.current || '')}})
}
3、vue-router在hash模式无法通过base设置基础路由
**解决方式:**创建一个空的路由页面,将其它路由作为它的children,具体设置如下:
import RootApp from './root-app.vue'const routes = [{path: window.__MICRO_APP_BASE_ROUTE__ || '/',component: RootApp,children: [// 其他的路由都写到这里],},
]
root-app.vue内容如下:
<template><router-view />
</template>
本文标签: micro
版权声明:本文标题:micro 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1687176969h72753.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论