什么是微前端
通俗易懂的来说,微前端是可以将一个大应用的不同部分进行独立的部署,各个部分之间相互独立,独立部署的能力允许他们构建孤立或松散耦合的服务。即将单页面前端应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。
微前端的核心设计理念
- 技术栈无关 主框架不限制接入应用的技术栈,微应用具备完全自主权
- 独立开发、独立部署 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
- 增量升级 在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
- 独立运行时 每个微应用之间状态隔离,运行时状态不共享
微前端的核心目标是将巨石应用拆解成若干个可以独立的子应用
qiankun是一个基于single-spa的微前端实现库
qiankun快速上手
qiankun官网https://qiankun.umijs.org/zh/ 具体的代码示例 github上有一些qiankun的示例代码https://github.com/zhjing1019/QianKunDemo
主应用
主应用不限技术栈,只需要提供一个容器 DOM,然后注册微应用并 start 即可。
代码语言:javascript复制yarn add qiankun # 或者 npm i qiankun -S
代码语言:javascript复制import { registerMicroApps, start } from 'qiankun'; /** * 在主应用中注册微应用 */ registerMicroApps([ { name: 'vue sub-app1', entry: '//localhost:7100/sub.html', container: '#yourContainer', activeRule: '/yourActiveRule', }, { name: 'vue sub-app2', entry: '//localhost:7101', container: '#yourContainer', activeRule: '/yourActiveRule', }, ], { beforeLoad: [ app => { console.log('before load', app); }, ], beforeMount: [ app => { console.log('before mount', app); }, ], afterMount: [ app => { console.log('after mount', app); }, ], afterUnmount: [ app => { console.log('after unload', app); app.render({appContent: '', loading: false}); }, ], } ); start();
注册微应用
当微应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子 微应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出 bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。微应用不需要额外安装任何其他依赖即可接入 qiankun 主应用。
代码语言:javascript复制/** * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。 * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。 */ export async function bootstrap() { console.log('react app bootstraped'); } /** * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法 */ export async function mount(props) { ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root')); } /** * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例 */ export async function unmount(props) { ReactDOM.unmountComponentAtNode( props.container ? props.container.querySelector('#root') : document.getElementById('root'), ); } /** * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效 */ export async function update(props) { console.log('update props', props); }
主应用和子应用项目通信
initGloabalState(state) 定义全局状态,并返回通信方法,官方建议在主应用使用,微应用通过 props 获取通信方法。 MicroAppStateActions onGlobalStateChange: (callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) => void, 在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback setGlobalState: (state: Record) => boolean, 按一级属性设置全局状态,微应用中只能修改已存在的一级属性 offGlobalStateChange: () => boolean,移除当前应用的状态监听,微应用 umount 时会默认调用
主应用:
代码语言:javascript复制import { initGlobalState, MicroAppStateActions } from 'qiankun'; // 初始化 state const actions: MicroAppStateActions = initGlobalState(state); actions.onGlobalStateChange((state, prev) => { // state: 变更后的状态; prev 变更前的状态 console.log(state, prev); }); actions.setGlobalState(state); actions.offGlobalStateChange();
子应用
代码语言:javascript复制// 从生命周期 mount 中获取通信方法,使用方式和 master 一致 export function mount(props) { props.onGlobalStateChange((state, prev) => { // state: 变更后的状态; prev 变更前的状态 console.log(state, prev); }); props.setGlobalState(state); }
js变量隔离
每个子应用都有相应的生命周期,同一时间内,只会有一个子应用的实例生效。js沙箱封装在qiankun的生命周期中。当一个子应用被销毁,其js沙箱也就被销毁。唯一不足的地方是,window的对象,无法隔离,最好不要绑定原型。
css污染
1、scoped(暂时解决方案:主应用的样式使用特殊class或者scoped) 2、主应用可以通过设置 prefixCls 的方式避免冲突 3、配置 webpack 修改 less 变量
代码语言:javascript复制{ loader: 'less-loader', options: { modifyVars: { '@ant-prefix': 'yourPrefix', }, javascriptEnabled: true, }, }
qiankun的具体详细解析源码 https://www.jianshu.com/p/db08174fa4fc?utm_campaign=shakespeare