">
我们需要父应用加载子应用,需要暴露三个方法:
1. bootstrap
2. mount
3. unmount
1. 构建子应用
// 启动项目安卓依赖vue create single-child npm i --save single-spa-vue// main.js中导入依赖 import singleSpaVue from ‘single-spa-vue‘ const appOptions = { el: ‘#vue‘, // 挂载到父应用中的 id 为 vue 的标签中 router, render: h => h(App) } const vueLifeCycle = singleSpaVue({ // 返回single-spa 的生命周期也就是 bootstrap/mount/unmount Vue, appOptions }); // single规定的协议,父应用会调用这些方法 export const bootstrap = vueLifeCycle.bootstrap; export const mount = vueLifeCycle.mount; export const unmount = vueLifeCycle.unmount; // 这样做还有一个严重的问题,子应用无法启动了??
2. 配置子应用中的打包路径
// 配置vue.config.js module.exports = { configureWebpack: { output: { library: ‘singleVue‘, libraryTarg: ‘umd‘ }, devServer: { port: 10000 } } };
3. 配置子应用的路由
const router = new VueRouter({ mode: ‘history‘, base: ‘/vue‘, // 配置路由的基础路径 routes })
4. 父应用搭建
vue create single-parent npm i --save single-spa // 注意这里是single-spa
5. 将子应用挂载到 id="vue"
的容器中
<div id="app"> <!-- 当路由切换到 /vue 时加载子应用 --> <router-link to="/vue">加载vue引用</router-link> <router-view/> <!-- 子应用加载的位置 --> <div id="vue"></div> </div>
6. 配置父应用加载子应用
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.onerror = reject; document.head.appendChild(script); }); } registerApplication( ‘myVueApp‘, async () => { console.log(‘加载模块‘); // 加载子应用中的脚本 await loadScript(`http://localhost:10000/js/chunk-vendors.js`) await loadScript(`http://localhost:10000/js/app.js`) // 这里需要要返回 bootstrap/mount/unmount return window.singleVue }, location => location.pathname.startsWith(‘/vue‘), // 此路径用来判断当前路由切换到 /vue 的路径下,需要加载我们定义的子应用 { a: 1 } // 选传,传给子应用 props 的参数,可以是对象或值 ); start(); // 启动应用
7. 配置子应用的路径
// 设置路径 if (window.singleSpaNavigate) { // 如果是父应用去应用,那会自动挂载一个属性为true __webpack_public_path__ = ‘http://localhost:10000/‘ }
8. 希望子应用可以独立运行,在子应用中添加一个配置
if(!window.singleSpaNavigate){ delete appOptions.el; // 子应用中没有#vue,所以需要手动删除,挂载到 #app 中 new Vue(appOptions).$mount(‘#app‘); }
乾坤完整的demo请参考:gitHub
1. 主应用搭建 qiankun-base
// 构建项目,下载依赖,只需要在主项目中安装 qiankun 即可vue create qiankun-base npm i --save qiankun// 配置主项目的加载 main.js import Vue from ‘vue‘ import App from ‘./App.vue‘ import router from ‘./router‘ import ElementUI from ‘element-ui‘; import ‘element-ui/lib/theme-chalk/index.css‘; import {registerMicroApps, start} from ‘qiankun‘; Vue.config.productionTip = false Vue.use(ElementUI); const apps = [ { name: ‘vueApp‘, // 应用的名字 entry: ‘http://localhost:10000/‘, // 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch) container: ‘#vue‘, // 要渲染到的容器名id activeRule: ‘/vue‘ // 通过哪一个路由来激活 }, { name: ‘reactApp‘, entry: ‘http://localhost:20000/‘, container: ‘#react‘, activeRule: ‘/react‘ } ]; registerMicroApps(apps); // 注册应用 start(); // 开启应用 new Vue({ router, render: h => h(App) }).$mount(‘#app‘)
<!-- 设置容器 --> <template> <div> <el-menu :router="true" mode="horizontal"> <!-- 主应用中也可以放自己的路由 --> <el-menu-item index="/">首页</el-menu-item> <!-- 引用其他的子应用 --> <el-menu-item index="/vue">vue应用</el-menu-item> <el-menu-item index="/react">react应用</el-menu-item> </el-menu> <router-view v-show="$route.name"></router-view> <div id="vue"></div> <div id="react"></div> </div> </template>
2. 搭建Vue子项目
vue create qiankun-vue // 子项目中不需要安装任何依赖,父组件会给window设置一些环境变量 // mian.js import Vue from ‘vue‘ import App from ‘./App.vue‘ import router from ‘./router‘ Vue.config.productionTip = false /* new Vue({ router, render: h => h(App) }).$mount(‘#app‘) */ let instance = null; function render(props) { // props 组件通信 instance = new Vue({ router, render: h => h(App) }).$mount(‘#app‘) // 这里是挂载到自己的HTML中,基座会拿到这个挂载后的HTML,将其插入进去 } if (!window.__POWERED_BY_QIANKUN__) { // 如果是独立运行,则手动调用渲染 render(); } if(window.__POWERED_BY_QIANKUN__){ // 如果是qiankun使用到了,则会动态注入路径 __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } // 根据 qiankun 的协议需要导出 bootstrap/mount/unmount export async function bootstrap(props) { }; export async function mount(props) { render(props); }; export async function unmount(props) { instance.$destroy(); };
// 设置router路径 const router = new VueRouter({ mode: ‘history‘, base: ‘/vue‘, routes })
// 配置打包 vue.config.js module.exports = { devServer: { port: 10000, headers:{ ‘Access-Control-Allow-Origin‘: ‘*‘ // 允许跨域 } }, configureWebpack: { output: { library: ‘vueApp‘, libraryTarget: ‘umd‘ } } };
3. 搭建React项目
npx create-react-app qiankun-react npm i --save-dev react-app-rewired // 入口配置 /src/index.js import React from ‘react‘; import ReactDOM from ‘react-dom‘; import ‘./index.css‘; import App from ‘./App‘; function render(){ ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById(‘root‘) ); } if(!window.__POWERED_BY_QIANKUN__){ render(); } export async function bootstrap(){ } export async function mount() { render() } export async function unmount(){ ReactDOM.unmountComponentAtNode( document.getElementById(‘root‘)); }
// 配置启动 config-overrides.js module.exports = { webpack:(config)=>{ config.output.library = ‘reactApp‘; config.output.libraryTarget = ‘umd‘; config.output.publicPath = ‘http://localhost:20000/‘; return config; }, devServer:(configFunction)=>{ return function (proxy,allowedHost){ const config = configFunction(proxy,allowedHost); config.headers = { "Access-Control-Allow-Origin":‘*‘ } return config } } }
添加react环境变量 .env PORT=20000 WDS_SOCKET_PORT=20000
// 配置react路由 import { BrowserRouter, Route, Link } from "react-router-dom" const BASE_NAME = window.__POWERED_BY_QIANKUN__ ? "/react" : ""; function App() { return ( <BrowserRouter basename={BASE_NAME}> <Link to="/">首页</Link> <Link to="/about">关于</Link> <Route path="/" exact render={() => <h1>hello home</h1>}></Route> <Route path="/about" render={() => <h1>hello about</h1>}></Route> </BrowserRouter> ); }
完整的项目请参考 gitHub