随着SPA大规模的应用,紧接着就带来一个新问题:一个规模化应用需要拆分。
一方面功能快速增加导致打包时间成比例上升,而紧急发布时要求是越短越好,这是矛盾的。另一方面当一个代码库集成了所有功能时,日常协作绝对是非常困难的。
而且最近十多年,前端技术的发展是非常快的,每隔两年就是一个时代,导致同志们必须升级项目甚至于换一个框架。但如果大家想在一个规模化应用中一个版本做好这件事,基本上是不可能的。
最早的解决方案是采用iframe的方法,根据功能主要模块拆分规模化应用,子应用之间使用跳转。但这个方案最大问题是导致页面重新加载和白屏。
那有什么好的解决方案呢?微前端这样具有跨应用的解决方案在此背景下应运而生了!
微前端的概念
微前端是什么:微前端是一种类似于微服务的架构,是一种由独立交付的多个前端应用组成整体的架构风格,将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的应用,而在用户看来仍然是内聚的单个产品。有一个基座应用(主应用),来管理各个子应用的加载和卸载。
所以微前端不是指具体的库,不是指具体的框架,不是指具体的工具,而是一种理想与架构模式。
微前端的核心三大原则就是:独立运行、独立部署、独立开发
微前端的优势
采用微前端架构的好处就是,将这些小型应用融合为一个完整的应用,或者将原本运行已久、没有关联的几个应用融合为一个应用可以将多个项目融合为一,又可以减少项目之间的耦合,提升项目扩展性。
实现微前端的几种方式
- 从single-spa到qiankun
- 基于WebComponent的micro-app
- webpack5实现的Module Federation
微前端框架的分类
Single-spa
single-spa
是一个很好的微前端基础框架,而qiankun
框架就是基于single-spa
来实现的,在single-spa
的基础上做了一层封装,也解决了single-spa
的一些缺陷。
首先我们先来了解该如何使用single-spa
来完成微前端的搭建。
Single-spa实现原理
首先在基座应用中注册所有App的路由,single-spa
保存各子应用的路由映射关系,充当微前端控制器Controler,。URL响应时,匹配子应用路由并加载渲染子应用。上图便是对single-spa
完整的描述。
有了理论基础,接下来,我们来看看代码层面时如何使用的。
以下以Vue工程为例基座构建single-spa,在Vue工程入口文件main.js完成基座的配置。
//main.jsimport Vue from 'vue'import App from './App.vue'import router from './router'import { registerApplication, start } from 'single-spa'Vue.config.productionTip = falseconst mountApp = (url) => { return new Promise((resolve, reject) => { const script = document.createElement('script') script.src = url script.onload = resolve script.onerror = reject // 通过插入script标签的方式挂载子应用 const firstScript = document.getElementsByTagName('script')[0] // 挂载子应用 firstScript.parentNode.insertBefore(script, firstScript) })}const loadApp = (appRouter, appName) => { // 远程加载子应用 return async () => { //手动挂载子应用 await mountApp(appRouter + '/js/chunk-vendors.js') await mountApp(appRouter + '/js/app.js') // 获取子应用生命周期函数 return window[appName] }}// 子应用列表const appList = [ { // 子应用名称 name: 'app1', // 挂载子应用 app: loadApp('http://localhost:8083', 'app1'), // 匹配该子路由的条件 activeWhen: location => location.pathname.startsWith('/app1'), // 传递给子应用的对象 customProps: {} }, { name: 'app2', app: loadApp('http://localhost:8082', 'app2'), activeWhen: location => location.pathname.startsWith('/app2'), customProps: {} }]// 注册子应用appList.map(item => { registerApplication(item)}) // 注册路由并启动基座new Vue({ router, mounted() { start() }, render: h => h(App)}).$mount('#app')
构建基座的核心是:配置子应用信息,通过registerApplication注册子应用,在基座工程挂载阶段start启动基座。
子应用配置
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import singleSpaVue from 'single-spa-vue'
Vue.config.productionTip = false
const appOptions = {
el: '#microApp',
router,
render: h => h(App)
}
// 支持应用独立运行、部署,不依赖于基座应用
// 如果不是微应用环境,即启动自身挂载的方式
if (!process.env.isMicro) {
delete appOptions.el
new Vue(appOptions).$mount('#app')
}
// 基于基座应用,导出生命周期函数
const appLifecycle = singleSpaVue({
Vue,
appOptions
})
// 抛出子应用生命周期
// 启动生命周期函数
export const bootstrap = (props) => {
console.log('app2 bootstrap')
return appLifecycle.bootstrap(() => { })
}
// 挂载生命周期函数
export const mount = (props) => {
console.log('app2 mount')
return appLifecycle.mount(() => { })
}
// 卸载生命周期函数
export const unmount = (props) => {
console.log('app2 unmount')
return appLifecycle.unmount(() => { })
}
配置子应用为umd打包方式//vue.config.js
const package = require('./package.json')
module.exports = {
// 告诉子应用在这个地址加载静态资源,否则会去基座应用的域名下加载
publicPath: '//localhost:8082',
// 开发服务器
devServer: {
port: 8082
},
configureWebpack: {
// 导出umd格式的包,在全局对象上挂载属性package.name,基座应用需要通过这个
// 全局对象获取一些信息,比如子应用导出的生命周期函数
output: {
// library的值在所有子应用中需要唯一
library: package.name,
libraryTarget: 'umd'
}
}
配置子应用环境变量// .env.micro
NODE_ENV=development
VUE_APP_BASE_URL=/app2
isMicro=true
子应用配置的核心是用singleSpaVue生成子路由配置后,必须要抛出其生命周期函数。
用以上方式便可轻松实现一个简单的微前端应用了。
那么我们有single-spa
这种微前端解决方案,为什么还需要qiankun
呢?
相比于single-spa
,qiankun
他解决了JS沙盒环境,不需要我们自己去进行处理。在single-spa
的开发过程中,我们需要自己手动的去写调用子应用JS的方法(如上面的 createScript方法),而qiankun
不需要,乾坤只需要你传入响应的apps的配置即可,会帮助我们去加载。
暂无评论内容