2018年single-spa诞生了,single-spa是一个用于前端微服务化的js前端解决方案(本身没有处理样式隔离,js执行隔离),实现了路由劫持和应用加载
2019年qiankun基于single-spa,提供了更加开箱即用的API(single-spa + sandbox + import-html-entry)做到了与技术栈无关、并且接入简单
总结:子应用可以独立构建,运行时动态加载,主子应用完全解耦,与技术栈无关,靠的是协议接入,子应用必须导出bootstrap、mount、unmount方法。
如果使用iframe,iframe中的子应用切换路由时用户刷新页面就尴尬了。
1、基于URL来进行数据传递,但是传递消息能力弱
2、基于CustomEvent实现通信
3、使用全局变量、Redux进行通信
1、CDN - externals
2、webpack 联邦模块
1、先创建一个vue项目:vue create child-vue
2、在此项目中添加single-spa-vue:npm i single-spa-vue
说明:如果是react项目则安装single-spa-react
3、修改main.js文件,导出bootstrap/mount/unmount:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import singleSpa from 'single-spa-vue';
Vue.config.productionTip = false
const appOptions = {
el: '#vue', // 挂载到父应用中的id为vue的标签中
router,
render: h => h(App)
}
const vueLifeCycle = singleSpa({
Vue,
appOptions
})
// 如果是父应用引用我,就会有这个属性
if(window.singleSpaNavigate){
__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给父应用使用
// 子应用需要导出 bootstrap mount unmount
// single-spa single-spa-vue single-spa-react
4、在根目录下添加vue.config.js,将此应用导出一个lib:
module.exports = {
configureWebpack: {
output: {
library: 'singleVue',
libraryTarget: 'umd'
},
devServer: {
port: 10000
}
}
}
// umd格式就是会把导出的内容挂载在window.singleVue.bootstrap/mount/unmount上面
5、修改router下面的index.js,让路由跳转的时候基于它自己,这样就不会和父应用冲突了:
const router = new VueRouter({
mode: 'history',
base: '/vue',
routes
})
6、然后创建父应用:vue create parent-vue
7、在此应用中添加single-spa:npm i single-spa
8、修改App.vue:
加载vue子应用
9、修改main.js,动态去加载子应用:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import { registerApplication, start } from 'single-spa';
Vue.config.productionTip = false
async function loadScript(url){
return new Promise((resolve, reject) => {
let script = document.createElement('script');
script.src = url;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
})
}
registerApplication('myVueApp',
async () => {
// 加载vue子模块
console.log('加载')
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();
new Vue({
router,
render: h => h(App)
}).$mount('#app')
自己实现的小demo