mkdir myapp && cd myapp yarn add @umijs/plugin-qiankun -D
yarn add @umijs/plugin-qiankun -D
import { defineConfig } from 'umi'; export default defineConfig({ title: '乾坤微应用demo', layout:{ name: '乾坤微应用demo', }, qiankun: { slave:{}, master: { // 注册子应用信息 apps: [ { name: 'vue-app', // 唯一 id entry: '//localhost:9001', props:{}, }, { name: 'react-app', // 唯一 id entry: '//localhost:9000', props:{}, }, ], }, }, fastRefresh:{}, nodeModulesTransform: { type: 'none', }, routes: [ { path: '/home', component: '@/pages/index' }, // 配置微应用 app1 关联的路由 { path: '/vue-app', name: 'vue子应用', microApp: 'vue-app', props: { onClick: (e: any)=>console.log(e), name: 'vue子应用', }, }, { path: '/react-app', name: 'react子应用', microApp: 'react-app', props: { onClick: (e: any)=>console.log(e), name: 'react子应用', }, }, ], });
由于vue-cli高版本有许多问题,这里选择4以下的版本进行安装
npm i [email protected]
配置vue.config.js如下:
const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const {VueLoaderPlugin} = require("vue-loader"); module.exports = { entry: { main: path.join(__dirname, 'src', 'main.ts'), }, output: { library: 'vue-app-[name]', libraryTarget: 'umd', jsonpFunction: 'webpackJsonp_vue', }, module: { rules: [ { test: /\.(tsx|ts)?$/, use: [ { loader: 'ts-loader', options: { transpileOnly: true, //只进行语法转换,不进行类型校验,提高构建速度 } } ], exclude: [/node_modules/], }, { test: /\.(js|jsx)$/, // 如果js文件在node_modules里边,就不使用babel-loader了 // 因为它里边的代码都是些第三方代码,已经做好了转译的工作。 exclude: [/node_modules/], use: [{ loader: "babel-loader", options: { //将async/await转化为es5语法,需要用到regeneratorRuntime函数,这个函数在babel-runtime包中 plugins: ["@babel/plugin-transform-runtime"] }, }, ], }, { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.less$/,//处理.less文件 use: ["style-loader", "css-loader", "less-loader"], }, { test: /\.html$/, loader: 'html-loader', options: { esModule: false } }, { //处理图片资源 test: /\.(png|jpg|jpeg|gif|bmp|stl|fbx|gltf)$/, loader: 'url-loader', options: { //图片小于1024kb,就会被base64处理,一般处理8-12KB的图片 //优点: 减少请求数量 //缺点: 图片体积会变大 // limit: 1024 * 1024, } }, { test: /\.vue$/, loader: 'vue-loader', } ] }, plugins: [ //生成一个 HTML5 文件, 其中包括使用 script 标签的 body 中的所有 webpack 包。 new HtmlWebpackPlugin({ title: 'vue-app子应用', template: path.resolve(__dirname, "src/index.html"), filename: "index.html" }), new VueLoaderPlugin(), ], resolve: { extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.png', 'vue'], alias: { "@": path.resolve(__dirname, "src"), } }, devServer: { hot: true,//热更新 compress: true, host: "127.0.0.1", open: false, port: 9001, headers: { 'Access-Control-Allow-Origin': '*', }, } }
我安装的依赖如下:
"dependencies": { "core-js": "^2.6.5", "vue": "^2.6.10", "vue-router": "^3.0.3", "vuex": "^3.0.1", "webpack": "^4.41.5", "webpack-cli": "^3.3.10", "webpack-dev-server": "^3.10.1" }, "devDependencies": { "@umijs/plugin-qiankun": "^2.43.1", "@vue/cli-plugin-babel": "^3.12.0", "@vue/cli-plugin-typescript": "^3.12.0", "@vue/cli-service": "^3.12.0", "@babel/plugin-transform-runtime": "^7.16.8", "compression-webpack-plugin": "^9.2.0", "file-loader": "^6.2.0", "glob": "^7.2.0", "happypack": "^5.0.1", "html-loader": "1.3.0", "html-webpack-plugin": "^4.4.1", "less": "^4.1.2", "less-loader": "^7.3.0", "mockjs": "^1.1.0", "mockjs-fetch": "^1.0.3", "thread-loader": "^3.0.4", "ts-loader": "^4.0.0", "vue-loader": "15.10.1", "typescript": "^4.5.4", "url-loader": "^4.1.1", "style-loader": "^2.0.0", "css-loader": "^0.23.1", "webpack-node-externals": "^3.0.0", "vue-template-compiler": "^2.6.10" },
name属性要与父工程中注册的子应用名保持一致。
我这里使用的是vue-app。
{ "name": "vue-app", },
yarn add @umijs/plugin-qiankun -D
我这里的入口文件是main.ts,与webapck中配置的entry一致。
main.ts
import Vue from 'vue'; import App from './App.vue'; import routes from './router'; import store from './store'; import VueRouter from 'vue-router'; Vue.use(VueRouter); Vue.config.productionTip = false; let route:any; let instance:any; function render(props = {}) { // @ts-ignore const {container} = props; route = new VueRouter({ base: window.__POWERED_BY_QIANKUN__ ? '/qiankun/vue-app' : '/', mode: 'history', routes: routes, }); instance = new Vue({ router: route, // store, render: (h) => h(App), }).$mount(container ? container.querySelector('#app') : '#app'); } if (!window.__POWERED_BY_QIANKUN__) { render(); } export async function bootstrap() { console.log('vue子应用bootstrap'); } export async function mount(props: {}) { console.log('vue子应用mount'); render(props); } export async function unmount(props: {}){ console.log('vue子应用unmount', props); instance.$destroy(); instance.$el.innerHTML = ''; instance =null; route = null; }
router.ts
import Home from './views/Home.vue'; import About from './views/About.vue'; let routes = [ { path: '/', name: 'home', component: Home, }, { path: '/about', name: 'about', component: About, }, ] export default routes;
注意上面component中组件引入方式如果使用懒加载方式,会报错找不到chunk包。
react子应用部署步骤与vue类似,除开乾坤生命周期写法有些不同外,然后就是基础路由替换方式有些不同,其他没有什么区别。
main.js中添加
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import reportWebVitals from './reportWebVitals'; import './index.less'; import './public-path'; import { BrowserRouter } from 'react-router-dom'; const initApp = container =>{ ReactDOM.render(, container?container.querySelector('#root'):document.getElementById('root') ); } if(!window.__POWERED_BY_QIANKUN__){ initApp(); } export async function bootstrap() { console.log('react子应用bootstrap'); } export async function mount(props) { const {container} = props; console.log('react子应用挂载', props); initApp(container); } export async function unmount(props){ console.log('react子应用unmount', props); const {container} = props; ReactDOM.unmountComponentAtNode(container.querySelector('#root')); }
public-path.js
if(window.__POWERED_BY_QIANKUN__){ __webpack_pubilc_path = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ }
如果不替换基础路由,可能会出现在react项目本地可以访问,但是乾坤主应用上面会提示匹配不到路由。
这里我采用的是在routes文件夹里对所有路由前面加一个变量base,例如:
const base = window.__POWERED_BY_QIANKUN__ ? "qiankun/react-app" : ''; const routes = [ { path: '/', component: () => import('../route/Demo') }, ] const exportRoutes = routes.map(item => { return { path: base + item.path, component: asyncComponent(item.component) //asyncComponent 处理异步加载组件 } })
如果当当前环境处于乾坤主应用环境时候,给所有路由加一个"qiankun/react-app",这样就能匹配上路由了
解决办法: 替换对应loader和plugin版本。
解决办法:仔细观察看是插件和loader版本不兼容问题,还是缺少插件和loader去处理某个资源文件。缺少的话就手动添加一下在重新启动。
解决方法: 修改devserver版本
解决办法:安照上面修改main.ts里面的方法,给乾坤环境添加生命周期函数并导出。
解决办法:检查webapck配置输出,按照上面vue.config.js配置output中,library设置为 '子应用名-[name]'。
output: { library: 'vue-app-[name]', libraryTarget: 'umd', jsonpFunction: 'webpackJsonp_vue', },