当前位置: 首页 > 科技观察

基于乾坤微前端实践——从零到一

时间:2023-03-13 22:34:08 科技观察

小结:微前端痛点及问题解决1、使用背景1)可以使用多种前端框架(React、AngularJS、Vue等)在同一页上;2)使用新框架编写新代码,无需重写现有app;3)延迟加载代码可以减少初始加载时间;2.主要需要解决的问题:1)一个APP中的不同模块由不同的团队维护,每个团队使用的技术栈可能不同,发布周期也不同。2)升级使用的前端框架的负担。新版本中可能存在不兼容的更新。升级后,现有业务可能会出现BUG,导致升级困难。限制使用新版本的前端框架。1、为什么需要微前端《~微前端图~》我们用3w(what、why、ho??w)来解释微前端1、what?什么是微前端?微前端是根据不同的维度将不同的功能拆分成多个子应用。这些子应用程序由主应用程序加载。微前端的核心就是把它拆了,拆完了再组装!微前端架构有以下核心价值:(重要)(摘自qiankun官方文档)1)技术栈无关的主框架不限制应用程序技术栈的访问,微应用具有完全的自主性;2)独立开发、独立部署微应用仓库独立,前后端可独立开发,部署完成后主框架自动同步更新;现有系统的整个技术栈很难升级或重构,微前端是实现渐进重构的一个很好的手段和策略;4)独立运行时各个微应用之间的状态隔离,运行时状态不共享;2.为什么?为什么要使用它?1)不同的团队如何以不同的方式开发相同的应用技术栈?2)希望每个团队都能独立开发,独立部署怎么破?3)Project如何破解旧的应用程序代码?我们能不能把一个应用程序分成几个子应用程序,然后把子应用程序打包成一个个的库。切换路径时加载不同的子应用程序。这样每个子应用都是独立的,不需要限制技术栈!这样就解决了前端协同开发的问题。3.如何?如何落地微前端?2018年,Single-SPA诞生。single-spa是一个针对前端微服务(不处理样式隔离、js执行隔离)实现路由劫持和应用加载的JavaScript前端解决方案。说明:single-spa解决了路由、应用注册、监控,最重要的是,应用生命周期和生命周期相关的事件。*Single-SPA缺陷:动态加载js文件不够灵活;样式不隔离,没有js沙箱机制。2019年,qiankun是一个微前端框架,提供了更多开箱即用的API(single-spa+sandbox+import-html-entry),基于single-spa,带有js沙箱,样式隔离,HTMLLoader,预加载微前端系统所需的能力。qiakun升级到2.0后,支持同时加载多个小程序。有了这个功能,我们基本上可以像访问iframe一样轻松访问微应用。*总结:子应用可独立构建,运行时动态加载,主子应用完全解耦,技术栈独立,依赖协议访问(子应用必须导出bootstrap,mount,unmount方法)扩展:1)Single-SPA官网地址:https://zh-hans.single-spa.js.org/docs/getting-started-overview2)乾坤官网地址:https://qiankun.umijs.org/zh2.隔离的解决方案1.CSSIsolationscheme子应用之间的样式隔离:DynamicStylesheet动态样式表,当应用切换时,移除旧的应用样式,添加新的应用样式;主应用和子应用之间的样式隔离:1)BEM(BlockElementModifier)契约项前缀;2)css-Modules在打包时生成不冲突的选择器名称;3)真正意义上的ShadowDOM隔离;4)css-in-js2,sandboxshadowDom*css解决方案://dom的api//外界无法访问shadowdomletshadowDOM=document.getElementById('x').attachShadow({mode:'closed'});让pElm=document.createElement('p');pElm.innerHTML='hello';让styleElm=document.createElement('style');styleElm.textContent=`p{color:red}`shadowDOM.appendchild(styleElm);shadowDOM.appendchild(pElm);*JSsandboxproxysnapshotSandbox简单理解:shot1yearago一个是拍照(保存差值),回到一年前的源码实践letsandbox=newSnapshotSandbox();类SnapshotSandbox{constructor(){this.proxy=window;//窗口属性this.modifyPropsmap={};//记录窗口上的修改this.active();}active(){//激活this.windowSnapshot={};//拍照for(constpropinwindow){if(window.hasOwnProperty(prop)){this.windowsnapshot[prop]=window[prop];}}object.keys(this.modifyPropsMap).forEach(p=>{window[p]=this.modifyPropsMap[p];})}inactive(){//inactivefor(constpropinwindow){if(window.hasOwnProperty(prop)){if(window[prop]!==this.windowsnapshot[prop]){this.modifyPropsMap[prop]=window[prop];window[prop]=this.windowsnapshot[prop]}}}}}//应用程序从头到尾运行,切换后整个世界不受影响((window)=>{window.a=1;window.b=2;console.log(window.a,window.b);sandbox.inactive();console.log(window.a,window.b);sandbox.active();console.log(window.a,window.b);})(sandbox.proxy);//sandbox.proxy是window//如果有多个子应用,则不能使用该方法。es6的proxy//代理沙箱可以实现多应用沙箱使用不同的代理来处理不同的应用3.乾坤(Qiankun)项目实践*将一个普通的项目转化为乾坤的主要应用基础需要三个步骤:1.创建一个微应用容器——用于承载微应用、Render和显示微应用2.注册微应用——设置微应用激活条件、微应用地址等;3、启动乾坤;扩展:主应用不局限于技术栈,只需要提供一个容器DOM,然后注册微应用并启动即可。*微前端qiankun项目实践:主应用(base)配置react17.0.2,子应用配置vue2.6.10。详细配置如下:1.主应用(base)配置react17.0.21.1。主应用为子应用准备显示元素(文件:src/App.js)import{BrowserRouterasRouter,Link}from'react-router-dom'functionApp(){return(vueapplication{/*切换导航,渲染容器中的微应用*/}

);}导出默认应用;1.2引入react渲染,registerApps(file:src/index.js)importReactfrom'react';importReactDOMfrom'react-dom';importAppfrom'./App';import'./registerApps'ReactDOM.render(,document.getElementById('root'));1.3在主应用中注册微应用(文件:src/registerApps.js)1.安装qiankun(推荐安装:qiankun2.X或以上版本,支持同时加载多个微应用)yarnaddqiankunornpmiqiankunrelated配置信息://------Step1introduceqiankunimport{registerMicroApps,start}from'qiankun';//底层基于single-spa//-----Step2注册子应用registerMicroApps([{na我:'m-vue',条目:'//localhost:20000',容器:'#container',activeRule:'/vue',},],{beforeLoad:()=>{console.log('loadbefore')},beforeMount:()=>{console.log('mountedbefore')},afterMount:()=>{console.log('aftermounted')},beforeUnmount:()=>{console.log('beforedestruction')},afterUnmount:()=>{console.log('afterdestruction')},})//-----Step3启动应用程序start();2.子应用配置vue2.6.10的主应用基础只有一个首页。现在我们需要通过import-entry-html来访问微应用qiankun来加载微应用,这就需要微应用导出生命周期钩子函数(见下图)。从上图可以看出,qiankun在内部验证了微应用的生命周期钩子函数。如果微应用没有导出这三个生命周期钩子函数,就会导致微应用加载失败。如果我们使用脚手架构建微应用,我们可以通过webpack配置在入口文件中导出这三个生命周期钩子函数。如果不使用脚手架,这三个生命周期钩子函数也可以直接挂载在小程序的窗口上。2.1调整子应用main.js文件:importVuefrom'vue'importAppfrom'./App.vue'importrouterfrom'./router'//Vue.config.productionTip=falseletinstance=nullfunctionrender(props){instance=newVue({router,render:h=>h(App)}).$mount('#app');//这里是dock在自己的html中挂载后会得到的插入到html中}if(window.__POWERED_BY_QIANKUN__){//动态添加publicPath__webpack_public_path__=window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}if(!window.__POWERED_BY_QIANKUN__){//默认独立运行render();}//需要暴露访问协议exportasyncfunctionbootstrap(props){console.log('[vue]vueappbootstrap');};exportasyncfunctionmount(props){console.log('[vue]propsfrommainframework',props);渲染(道具);}导出异步函数卸载(道具){实例。$销毁();instance.$el.innerHTML='';实例=空;router=null;}说明:导出对应的生命周期钩子函数。小应用需要在自己的入口js(一般是你配置的webpack的入口js)中导出bootstrap、mount、unmount三个生命周期钩子,以便主应用在合适的时候调用。*扩展资源:/***bootstrap只会在微应用初始化时调用一次,下次微应用重新进入时会直接调用mounthook,不会重复触发bootstrap。*通常我们可以在这里初始化一些全局变量,比如在unmount阶段不会被销毁的应用级缓存。*/exportasyncfunctionbootstrap(){console.log('[vue]vueappbootstraped');}/***(重要)每次应用进入都会调用mount方法,通常我们会触发rendering方法这里的应用程序*/exportasyncfunctionmount(props){console.log('[vue]propsfrommainframework',props);商店测试(道具);render(props);}/***每次应用被切出/卸载时调用的方法,通常这里我们会卸载微应用的应用实例*/exportasyncfunctionunmount(){instance.$destroy();instance.$el.innerHTML='';实例=空;router=null;}/***可选生命周期钩子,仅在使用loadMicroApp方法加载微应用时有效*/exportasyncfunctionupdate(props){console.log('updateprops',props);}2.2创建新建一个vue.config.js,配置如下module.exports={devServer:{port:10000,headers:{//解决跨域'Access-Control-Allow-Origin':'*'}},configureWebpack:{output:{//将子应用打包成umd库格式library:`${name}-[name]`,libraryTarget:'umd',jsonpFunction:`webpackJsonp_${name}`,}}}*Basedonqiankun微前端项目(实践代码库)https://github.com/jiasx/mic-front-vue2.0https://github.com/jiasx/mic-front-react