微前端是什么?
微前端就是将一个主应用按照不同功能拆分为不同的子应用,然后通过子应用来加载主应用。
使用微前端有什么好处呢?
- 可以在一个应用中使用多种技术栈。
- 一个主应用拆分为多个子应用,这些子应用可交给不同的团队进行开发。
- 对于项目中的老代码不宜改动,可使用微前端将其整体嵌入。
微前端发展史
- 2018年Sing-SPA实现了路由劫持和应用加载,但没有处理样式隔离和js执行隔离,不能动态加载js文件。
子应用中
// main.js
import singleSpaVue from 'sing-spa-vue'
const appOptions = {
el: '#vue', // 挂载到父应用中的id为vue的标签中
router,
render: h => h(App)
}
const vueLifeCycle = singSpaVue({
Vue,
appOptions
})
// 如果是父应用引用我
if(window.singleSpaNavigate) {
// 当引用该文件打包后的资源时,就会在路径前面加上__webpack_public_path_
__webpack_public_path_ = 'http://localhost:10000/'
}
// 保证子应用可以单独启动
if(!window.singleSpaNavigate) {
delete appOptions.el
new Vue(appOptions).$mount(#app)
}
// 协议接入,定义好了协议,父应用就会调用这些方法
export const bootstrap = vueLifeCycle.bootstrap
export const mount = vueLifeCycle.mount
export const unmount = vueLifeCycle.unmount
// 我们需要父应用去加载子应用,将子应用打包为一个个lib包给父应用使用
// vue.config.js
module.exports = {
configureWebpack: {
output: {
library: 'singleVue', // 打包出的类库名为sing-vue
libraryTarget: 'umd'
}
},
devServer: {
port: 10000
}
}
// router.js
const routes = [
{
path: '/home',
component: Home
},
{
path: '/about',
component: () => import(/*webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: '/vue',
routes
})
父应用
// main.js
import {
registerApplication,start } from 'single-spa'
async function loadScript(url) {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
script.src = url
script.onload = resolve
script.onerrror = reject
document.head.appendChild(script)
})
}
// 注册子应用
registerApplication('myVueApp',
async() => {
// console.log('加载模块')
// systems 加载子应用打包后生成的文件
await loadScript('http://localhost:10000/js/chunk-vendors.js')
await loadScript('http://localhost:10000/js/app.js')
return window.singleVue
},
location => location.pathname.startsWith('/vue') // 用户切换到/vue路径下,需要加载刚才定义的子应用
)
// 开启子应用
start()
- 2019年qiankun基于Single-SPA,实现了一个应用中可使用多种技术栈的功能,类似iframe。
- 子应用之间样式隔离
Dynamic Stylesheet 动态样式表,当应用切换时移除老的应用样式,添加新应用样式 - 主应用和子应用之间样式隔离的方式
BEM:约定项目前缀
CSS-Modules:打包时生成不冲突的选择器名
Shadow DOM:真正意义上的隔离
css-in-js
// main.js
import {
registerMicroApps, start } from 'qiankun'
const apps = [
{
name: 'vueApp', // 应用名
entry: '//localhost:10000'. // 默认会加载这个html,解析里面的js 动态的执行(子应用必须支持跨域)
container: '#vue', // 容器名
activeRule: '/vue' // 激活的路径
},
{
name: 'reactApp',
entry: '//localhost:20000'. // 默认会加载这个html,解析里面的js 动态的执行(子应用必须支持跨域)
container: '#react',
activeRule: '/react'
}
]
registerMicroApps(apps) // 注册应用
start() // 开启