一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。 前言: Iframe?Single-Spa?QianKun? 微前端很多,技术栈百花齐放,我们自己实现一波?
一、环境准备
1.1 使用rollup
npm init -y
npm i rollup rollup-plugin-serve
1.2 新建一个rollup.config.js
import serve from 'rollup-plugin-serve'
// rollup可以帮我们打包 es6的模块化语法
export default {
input: './src/single-spa.js',
output: {
file: './lib/umd/single-spa.js',
format: 'umd',
name: 'singleSpa',
sourcemap:true
},
plugins: [
serve({
openPage: '/index.html',
contentBase: '',
port: 3000
})
]
}
1.3 编写package.json脚本
-c 走rollup.config.js -w 热更新
"dev": "rollup -c -w"
1.4 测试运行rollup
npm run dev
rollup 环境运行成功
1.5 测试热更新
在single-spa.js中导出一个变量,lib/umd/single-spa.js会自动生成global的变量
export const a =1
二、注册应用的方法
2.1 新建一个index.html, 引入rollup打包好后的js
script src="/lib/umd/single-spa.js"></script>
2.2 调用注册微应用的方法和启动方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="/lib/umd/single-spa.js"></script>
<script>
// 参数1 注册应用的名字 参数2 加载app的方法必须返回一个pormise方法
singleSpa.registerApplication('app1',async () => {
// 这个函数需要返回结果
return {
bootstrap: async() => {
},
mount: async() => {
},
unmount: async() =>{
}
}
},
location => location.hash.startsWith('#/app1'),
{store: {name:'zf',age:10}}
)
// 启动这个应用
singleSpa.start()
</script>
</body>
</html>
2.3 新建src/appications/app.js
/**
*
* @param {*} appName 应用名字
* @param {*} loadApp 加载引用
* @param {*} activeWhen 当激活时会调用 loadApp
* @param {*} customProps 自定义属性
*/
const apps = [] // 用来存放所有的应用
export function registerApplication (appName, loadApp, activeWhen, customProps) {
apps.push({
name: appName,
loadApp,
activeWhen,
customProps
})
console.log(apps)
}
2.4 新建src/start.js
export function start () {
}
2.5 最后在src/single-spa中导出registerApplication方法 和 start方法
export { registerApplication} from './applications/app'
export { start} from './start'
测试调用registerApplication方法的应用保存到啊数组中了
三、 微应用生命周期流程图
3.1 新建src/application/app.helper.js工具文件
// 描述应用的整个状态
export const NOT_LOADED = 'NOT_LOADED' //
export const LOADING_SOURCE_CODE = 'LOADING_SOURCE_CODE'
export const NOT_BOOTSTRAPPED = 'NOT_BOOTSTRAPPED'
export const BOOTSTRAPPING = 'BOOTSTRAPPING'
export const NOT_MOUNTED = 'NOT_MOUNTED'
export const MOUNTING = 'MOUNTING'
export const MOUNTED = 'MOUNTED'
export const UPDATING = 'UPDATING'
export const UNMOUNTING = 'UNDATING'
export const UNLOADING = 'UNLOADING'
export const LOAD_ERR = 'LOAD_ERR'
export const SKIP_BECAUSE_BROKEN = 'SKIP_BECAUSE_BROKEN'
// 当前应用是否被激活
export function isActive (app) {
return app.status === 'MOUNTED'
}
// 当前应用是否要被激活
export function shouldBeAcitve (app) { // true 引用就开始初始化等一系列操作
return app.activeWhen(window.location)
}
3.3 新建src/navigations/reroute.js重新加载路由
import { started } from "../start";
export function reroute () {
if (started) {
console.log('调用start方法');
} else {
console.log('调用register');
}
}
3.4 修改start.js
import { reroute } from "./navigations/reroute";
export let started = false
export function start () {
// 需要挂载应用
started = true
reroute() // 重新加载路由
}
3.5 修改app.js 中注册完应用后重新加载路由
// 维护应用所有的状态 状态机
export function registerApplication (appName, loadApp, activeWhen, customProps) {
apps.push({
name: appName,
loadApp,
activeWhen,
customProps,
status: NOT_LOADED
})
console.log(apps)
reroute() // 加载应用
// vue 一系列的生命周期
}
测试先register后start ok!
四、 封装whenActive 路由改变加载微应用
4.1 src/app.js 暴露方法
export function getAppChanges () {
const appsTouUmount = [] // 要卸载的app
const appsToLoad = [] //要加载的app
const appsToMount = [] // 需要挂载的app
apps.forEach(app => {
// 需不需要加载
const appShouldBeActive = app.status !== SKIP_BECAUSE_BROKEN && shouldBeAcitve(app)
switch (app.status) {
case NOT_LOADED:
case LOADING_SOURCE_CODE:
if (appShouldBeActive) {
appsToLoad.push(app)
}
break
case NOT_BOOTSTRAPPED:
case BOOTSTRAPPING:
case NOT_MOUNTED:
if (appShouldBeActive) {
appsToMount.push(app)
}
break;
case MOUNTED:
if (!appShouldBeActive) {
appsTouUmount.push(app)
}
default:
break;
}
})
return { appsToLoad, appsToMount,appsTouUmount }
}
4.2 reroute.js 去调用方法
export function reroute () {
// 需要获取加载的应用
// 需要获取要被挂载的应用
// 哪些应用需要被卸载
const { appsToLoad, appsToMount, appsTouUmount } = getAppChanges()
console.log(appsToLoad, appsToMount, appsTouUmount);
if (started) {
console.log('调用start方法');
// app 装载
return performaAppChanges() // 根据路径来装载应用
} else {
// 注册应用时 需要预先加载
console.log('调用注册方法');
return loadApps()
console.log('调用register');
}
// 预加载应用
async function loadApps () {
}
// 根据路径来装载应用
async function performaAppChanges () {
}
}
Tips: 别忘了index.html中的location看路由是否需要别激活的回调方法