厦门网站建设2015威海网站制作怎么样
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
本文主要记录下如何接入 qiankun 微前端。主应用使用 vue2,子应用使用 vue3、react。
一、主应用
 主应用不限技术栈,只需要提供一个容器 DOM,然后注册微应用并 start 即可。
1、创建项目
// @vue/cli 5.0.4
 npm install @vue/cli -g
  
 vue create main-vue
主应用选择 vue2.x 版本。 具体创建步骤,便不在此一一叙述。

s
项目创建之后,配置路由,页面布局等。整体效果如下图。

2、安装 qiankun
npm i qiankun -S

3、 注册微应用并启动
新建微应用子列表文件 micros/app.js
// src/micros/app.js
 // 子应用列表
 const apps = [
   {
     name: 'vue2-app', // 子应用app name 推荐与子应用的package的name一致
     entry: '//localhost:8081/', // 子应用的入口地址,就是你子应用运行起来的地址
     container: '#micro-container', // 挂载子应用内容的dom节点 `# + dom id`【见上面app.vue】
     activeRule: '/vue2App' // 子应用的路由前缀
   },
   {
     name: 'vue3-app',
     entry: '//localhost:8082/',
     container: '#micro-container',
     activeRule: '/vue3App'
   },
   {
     name: 'react-app',
     entry: '//localhost:8083/',
     container: '#micro-container',
     activeRule: '/react'
   }
 ]
  
 export default apps
注册微应用
// src/micros/index.js
 import { addGlobalUncaughtErrorHandler, registerMicroApps, start} from 'qiankun'
 // 微应用的信息
 import apps from './app'
  
 /**
  * 注册微应用
  * 第一个参数 - 微应用的注册信息
  * 第二个参数 - 全局生命周期钩子
  */
 registerMicroApps(apps, {
   // qiankun 生命周期钩子 - 微应用加载前
   beforeLoad: (app) => {
     // 加载微应用前,加载进度条
     console.log('before load=====', app.name)
     return Promise.resolve()
   },
   // qiankun 生命周期钩子 - 微应用挂载后
   afterMount: (app) => {
     // 加载微应用前,进度条加载完成
     console.log('after mount=====', app.name)
     return Promise.resolve()
   }
 }
 )
  
 /**
  * 添加全局的未捕获异常处理器
  */
 addGlobalUncaughtErrorHandler((event) => {
   console.error(event)
   const { message: msg } = event
   if (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {
     console.error('微应用加载失败,请检查应用是否可运行')
   }
 })
  
 // 导出 qiankun 的启动函数
 export default start
配置主应用路由
// src/router/index.js
  
 import Vue from 'vue'
 import VueRouter from 'vue-router'
  
 Vue.use(VueRouter)
  
 const routes = [
   {
     path: '/',
     name: 'home',
     component: () => import('@/components/Home.vue'),
     children: [{
       path: '/',
       name: 'hello',
       component: () => import('@/views/HomeView.vue')
     },{
       path: '/vue2App',
       name: 'vue2App'
     }, {
       path: '/vue3App',
       name: 'vue3App'
     }, {
       path: '/vue3App/list',
       name: 'vueAppList'
     }, {
       path: '/react',
       name: 'react'
     }]
   }
 ]
  
 const router = new VueRouter({
   mode: 'history',
   base: process.env.BASE_URL,
   routes
 })
  
 export default router
页面设置子应用的挂载节点
<template>
   <div class="wrapper">
     <MyHeader></MyHeader>
     <el-container class="content">
       <el-aside width="200px">
         <MySider></MySider>
       </el-aside>
       <el-main>
         <router-view></router-view>
         <!-- 挂载子应用节点 -->
         <div id="micro-container"></div>
       </el-main>
     </el-container>
   </div>
 </template>
  
 <script>
   import MyHeader from './Header.vue'
   import MySider from './Sider.vue'
  
   export default {
     name: 'MyHome',
     components: {
       MyHeader,
       MySider
     },
     data() {
       return {
  
       }
     }
   }
 </script>
  
 <style lang="less">
   .content {
     height: calc(100% - 50px);
   }
 </style>
在 main.js 中引入并启动 qiankun
// src/main.js
  
 import Vue from 'vue'
 import App from './App.vue'
 import router from './router'
 import store from './store'
 import '../src/assets/style/reset.less'
 import './plugins/element.js'
  
 import start from '@/micros'
 // 启动
 start()
  
 Vue.config.productionTip = false
  
 new Vue({
   router,
   store,
   render: h => h(App)
 }).$mount('#app')
二、微应用
 微应用分为有 webpack 构建和无 webpack 构建项目,有 webpack 的微应用(主要是指 Vue、React、Angular)需要做的事情有:
新增 public-path.js 文件,用于修改运行时的 publicPath。
 微应用建议使用 history 模式的路由,需要设置路由 base,值和它的 activeRule 是一样的。
 在入口文件最顶部引入 public-path.js,修改并导出三个生命周期函数。
 修改 webpack 打包,允许开发环境跨域和 umd 打包。
 无 webpack 构建的微应用直接将 lifecycles 挂载到 window 上即可。
微应用无需安装 qiankun。
三、vue2-app 微应用
  1、创建项目
// vue-cli 2.9.6
 npm install vue-cli -g
  
 npm install webpack-cli -g
  
 npm init webpack vue-app
2、在 src 目录新增 public-path.js
// src/public-path.js
 if(window.__POWERED_BY_QIANKUN__) {
   __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
 }
  
修改路由文件,建议使用history 模式的路由,并设置路由 base,值和它的 activeRule 是一样的。
// src/router/index.js
 import Vue from 'vue'
 import Router from 'vue-router'
 import HelloWorld from '@/components/HelloWorld'
  
 Vue.use(Router)
  
 export default new Router({
   mode: 'history',
   base: window.__POWERED_BY_QIANKUN__ ? "/vue2App" : "/",
   routes: [
     {
       path: '/',
       name: 'HelloWorld',
       component: HelloWorld
     }
   ]
 })
入口文件 main.js 修改,为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围。并导出三个生命周期函数。
// src/main.js
 import Vue from 'vue'
 import App from './App'
 import router from './router'
 import "./public-path";
  
 Vue.config.productionTip = false
  
 // 定义一个Vue实例
 let instance = null
 // 渲染方法
 function render(props = {}) {
   const { container } = props
   instance = new Vue({
     router,
     render: (h) => h(App)
   }).$mount(container ? container.querySelector('#app'): '#app')
 }
 // 独立运行时
 if(!window.__POWERED_BY_QIANKUN__) {
   render()
 }
 //暴露主应用生命周期钩子
 /**
  * bootstrap : 在微应用初始化的时候调用一次,之后的生命周期里不再调用
  */
 export async function bootstrap() {
   console.log('vue2-app bootstraped');
 }
 /**
  * mount : 在应用每次进入时调用
  */
 export async function mount(props) {
   console.log('vue2-app mount', props);
   render(props);
 }
 /**
  * unmount :应用每次 切出/卸载 均会调用
  */
 export async function unmount() {
   console.log("vue2-app unmount")
   instance.$destroy();
   instance.$el.innerHTML = '';
   instance = null;
 }
修改 webpack 打包,允许开发环境跨域和 umd 打包。
// build/webpack.base.conf.js
 'use strict'
 const config = require('../config')
 const APP_NAME = require('../package.json').name
  
 module.exports = {
   output: {
     path: config.build.assetsRoot,
     filename: '[name].js',
     publicPath: process.env.NODE_ENV === 'production'
       ? config.build.assetsPublicPath
       : config.dev.assetsPublicPath,
     // 微应用的包名,这里与主应用中注册的微应用名称一致
     library: APP_NAME,
     // 将你的 library 暴露为所有的模块定义下都可运行的方式
     libraryTarget: "umd",
     // 按需加载相关,设置为 webpackJsonp_VueMicroApp 即可
     jsonpFunction: `webpackJsonp_${APP_NAME}`,
   },
   ...
 }
// build/webpack.dev.conf.js
 const devWebpackConfig = merge(baseWebpackConfig, {
    ...
    devServer: {
      ...
      // 关闭主机检查,使微应用可以被 fetch
      disableHostCheck: true,
      // 配置跨域请求头,解决开发环境的跨域问题
      headers: {
        "Access-Control-Allow-Origin": "*",
      }
    }
 })
运行效果如下:

四、vue3-app 微应用
 1、创建项目
// @vue/cli 5.0.4
 npm install @vue/cli -g
  
 vue create vue3-app

  2、在 src 目录新增 public-path.ts
// src/public-path.ts
 if ((window as any).__POWERED_BY_QIANKUN__) {
   __webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
 }
修改路由文件。
// src/router/index.ts
 import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
  
 const routes: Array<RouteRecordRaw> = [
   {
     path: "/",
     name: "home",
     component: () => import("@/components/Home.vue"),
     children: [
       {
         path: "/",
         name: "index",
         component: () => import("../views/HomeView.vue"),
       },
       {
         path: "/list",
         name: "list",
         component: () => import("../views/AboutView.vue"),
       },
     ],
   },
 ];
  
 const router = createRouter({
   history: createWebHistory(
     window.__POWERED_BY_QIANKUN__ ? "/vue3App" : process.env.BASE_URL
   ),
   routes,
 });
  
 export default router;
入口文件 main.ts 修改
// src/main.ts
 import Vue, { createApp } from "vue";
 import App from "./App.vue";
 import router from "./router";
 import store from "./store";
 import "./public-path.ts";
  
 // 定义一个Vue实例
 let instance: Vue.App<Element>;
 // 需要定义该接口,否则`/src/router/index.ts`无法使用`Window.__POWERED_BY_QIANKUN__`
 declare global {
   interface Window {
     __POWERED_BY_QIANKUN__?: string;
   }
 }
 interface IRenderProps {
   container: Element | string;
 }
  
 // 渲染方法
 function render(props: IRenderProps) {
   const { container } = props;
   instance = createApp(App);
   instance
     .use(store)
     .use(router)
     .mount(
       container instanceof Element
         ? (container.querySelector("#app") as Element)
         : (container as string)
     );
 }
 // 独立运行时
 if (!window.__POWERED_BY_QIANKUN__) {
   render({ container: "#app" });
 }
  
 //暴露主应用生命周期钩子
 /**
  * bootstrap : 在微应用初始化的时候调用一次,之后的生命周期里不再调用
  */
 export async function bootstrap() {
   console.log("vue3-app bootstraped");
 }
  
 /**
  * mount : 在应用每次进入时调用
  */
 export async function mount(props: any) {
   console.log("mount vue3-app", props);
   render(props);
 }
  
 /**
  * unmount :应用每次 切出/卸载 均会调用
  */
 export async function unmount() {
   console.log("unmount vue3-app app");
   instance.unmount();
 }
修改 webpack 打包,允许开发环境跨域和 umd 打包。
注意:webpack5 中 jsonpFunction 修改为 chunkLoadingGlobal
// vue.config.js
 const path = require("path");
  
 const APP_NAME = require("./package.json").name;
  
 function resolve(dir) {
   return path.join(__dirname, dir);
 }
  
 module.exports = {
   outputDir: "dist",
   assetsDir: "static",
   filenameHashing: true,
   devServer: {
     host: "localhost",
     hot: true,
     port: 8082,
     client: {
       overlay: {
         errors: true,
         warnings: false,
       },
     },
     // 配置跨域请求头,解决开发环境的跨域问题
     headers: {
       "Access-Control-Allow-Origin": "*",
     },
   },
   // 自定义webpack配置
   configureWebpack: {
     resolve: {
       alias: {
         "@": resolve("src"),
       },
     },
     output: {
       // 把子应用打包成 umd 库格式
       // // 微应用的包名,这里与主应用中注册的微应用名称一致
       library: APP_NAME,
       // 将你的 library 暴露为所有的模块定义下都可运行的方式
       libraryTarget: "umd",
       // 按需加载相关,设置为 webpackJsonp_微应用名称 即可
       chunkLoadingGlobal: `webpackJsonp_${APP_NAME}`,
     },
   },
 };
运行效果如下:

五、react-app 微应用
1、创建项目,以 create-react-app 生成的 react 17 项目为例,搭配 react-router-dom 6.x。
npx create-react-app react-app --template typescript
  
 npm i react-router-dom

在根目录下添加 .env 文件,设置项目监听的端口
// react-app/.env
 PORT=8083
 BROWSER=none
 2⃣️、新建 public-path.ts
// src/public-path.ts
 if ((window as any).__POWERED_BY_QIANKUN__) {
   __webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
 }
如上面代码报错,可以通过补充定义进行修复。以下代码最好放到全局引入的 TypeScript 定义文件中。
interface Window {
   __POWERED_BY_QIANKUN__?: string
   __INJECTED_PUBLIC_PATH_BY_QIANKUN__?: string
 }
  
 declare let __webpack_public_path__: string | undefined
设置 history 模式路由的 base
// src/App.tsx
 import React from 'react';
 import { Routes, Route, BrowserRouter} from 'react-router-dom'
 import './App.css';
 import Home from './components/home';
  
 function App() {
   return (
     <div>
       {/* 设置路由命名空间 */}
       <BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/react' : '/'}>
         <Routes>
           <Route path="/" element={<Home />} />
         </Routes>
       </BrowserRouter>
     </div>
   );
 }
  
 export default App;
修改入口文件 index.tsx
// src/index.tsx
 import React from 'react';
 import ReactDOM from 'react-dom';
 import './index.css';
 import App from './App';
 import reportWebVitals from './reportWebVitals';
 import './types.d.ts'
 import "./public-path";
  
 /**
  * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
  * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
  */
 export async function bootstrap() {
   console.log('react-app bootstraped');
 }
 /**
  * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
  */
 export async function mount(props: any) {
   console.log('react-app mount');
   ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
 }
 /**
  * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
  */
 export async function unmount(props: any) {
   console.log('react-app unmount');
   ReactDOM.unmountComponentAtNode(props.container ? props.container.querySelector('#root') : document.getElementById('root'));
 }
 /**
  * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
  */
 export async function update(props: any) {
   console.log('react-app update props', props);
 }
  
 ReactDOM.render(
   <React.StrictMode>
     <App />
   </React.StrictMode>,
   document.getElementById('root')
 );
  
 // If you want to start measuring performance in your app, pass a function
 // to log results (for example: reportWebVitals(console.log))
 // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
 reportWebVitals();
修改 webpack 配置,安装插件 react-app-rewired
npm install react-app-rewired -D
修改 package.json
// react-app/package.json
 "scripts": {
   "start": "react-app-rewired start",
   "build": "react-app-rewired build",
   "test": "react-app-rewired test",
   "eject": "react-app-rewired eject"
 }
在 react-app-rewired 配置完成后,新建 config-overrides.js 文件来配置 webpack。
// react-app/config-overrides.js
 const path = require("path");
 const APP_NAME = require("./package.json").name;
  
 module.exports = {
   webpack: (config) => {
     // 微应用的包名,这里与主应用中注册的微应用名称一致
     config.output.library = APP_NAME;
     // 将你的 library 暴露为所有的模块定义下都可运行的方式
     config.output.libraryTarget = "umd";
     // 按需加载相关,设置为 webpackJsonp_VueMicroApp 即可
     config.output.chunkLoadingGlobal = `webpackJsonp_${APP_NAME}`;
     config.output.globalObject = 'window';
     config.output.publicPath = `//localhost:${process.env.PORT}/`;
  
     config.resolve.alias = {
       ...config.resolve.alias,
       "@": path.resolve(__dirname, "src"),
     };
     return config;
   },
  
   devServer: function (configFunction) {
     return function (proxy, allowedHost) {
       const config = configFunction(proxy, allowedHost);
       // 关闭主机检查,使微应用可以被 fetch
       // config.disableHostCheck = true;
       config.allowedHosts = "all";
       // 配置跨域请求头,解决开发环境的跨域问题
       config.headers = {
         "Access-Control-Allow-Origin": "*",
       };
       // 配置 history 模式
       config.historyApiFallback = true;
  
       return config;
     };
   },
 };
3、运行效果如下

 原文链接:https://blog.csdn.net/lhz_333/article/details/123767105
