• 乾坤qiankun-微前端-简单搭建demo


    Micro-frontend,微前端,顾名思义,前端也可以像后端一样通过多个微服务,搭建项目。只是后端的微服务架构是Spring Boot, 而前端我们可以使用qiankun实现前端的微服务架构。轻松现实大型项目的迭代重构升级以及拓展,不受牵一发而动全身的限制。下面介绍简单搭建微前端框架。
    项目下载地址 demo

    一.项目架构

     ├── base    # 主服务 - 可以是任意框架vue/react/angular/原生....
       ├── public   # 资源
       └── src  # 主要code
               └──  main.js 配置注册微服务信息,以及给子应用传参等...
    ├── app1-vue      # 微服务app1 - vue
       ├── public   # 资源
       ├── src  # 主要code
                  └── main.js  # 配置 注册信息 bootstrap/mount/unmount 方法声明
       └── vue.config.js  # 配置允许跨域,以及端口
    ├── app2-react    # 微服务app2 - react
       ├── public   # 资源
       └── src  # 主要code
                └── index.js 配置 注册信息 bootstrap/mount/unmount 方法声明
        └── config-overrides.js  # 配置允许跨域,以及端口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    二.搭建过程

    1.创建vue主应用
    创建Vue工程作为主服务,当然也可以是任意框架vue/react/angular/原生项目作为主应用。

    vue create base
    
    • 1

    在这里插入图片描述

    安装 qiankun

    npm install qiankun -s
    
    • 1

    在这里插入图片描述
    在主应用vue中配置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';
    Vue.use(ElementUI);
    import {
        registerMicroApps, // 注册应用
        start, // 开启方法
    } from 'qiankun';
    const apps = [
        {
            name: 'vueApp', // 应用名字,这个是微应用app1
            entry: '//localhost:10000', //默认会加载这个html 解析里面的js(这里使用的fetch) 动态的执行(子应用必须支持跨域)
            container: '#vue', //容器名 挂载元素
            activeRule: '/vue', // 激活路径 激活规则,当访问 /vue时就会把这个应用挂载到 #vue上
            props: { a: 1 }, // 实现主应用给子应用传参
        },
        {
            name: 'reactApp', // 应用名字,这个是微应用app2
            entry: '//localhost:20000',
            container: '#react',
            activeRule: '/react',
            props: { b: 2 }, // 实现主应用给子应用传参
        },
    ];
    registerMicroApps(apps); // 注册应用
    start({
        // 开启
        prefetch: false, // 取消预加载
    });
    
    Vue.config.productionTip = false;
    
    new Vue({
        router,
        render: h => h(App),
    }).$mount('#app-base'); // 这里需要更改挂载节点,防止跟app1-vue的节点重名。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    注意:根节点与Vue子应用重名将会报错,这里将根节点改为app-base,对应的index.html和app.vue也需要将id="app"改为id=“app-base”。具体改动如下:

    • public/index.html中,把
      改成
    • src/App.vue中,把
      改成

      public/index.html改动如下:
    <!DOCTYPE html>
    <html lang="">
        <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" />
            <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
            <title><%= htmlWebpackPlugin.options.title %></title>
        </head>
        <body>
            <noscript>
                <strong>
                    We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without
                    JavaScript enabled. Please enable it to continue.
                </strong>
            </noscript>
            <!-- <div id="app"></div> -->
            <div id="app-base"></div> // 这里需要把app改成app-base,防止跟app1-vue的挂载节点重名
            <!-- built files will be auto injected -->
        </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    App.vue中需要改动如下:

    <template>
      <div id="app-base"> 
        <img alt="Vue logo" src="./assets/logo.png">
        <HelloWorld msg="Welcome to Your Vue.js App"/>
        <el-menu default-active="/" :router="true" mode="horizontal">
          <el-menu-item index="/">base</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></router-view>
        <div id="vue"></div>
        <div id="react"></div>
      </div>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.创建app1-vue 子应用
    按照上述创建主应用Vue工程创建一个vue项目

    注意 vite目前暂不支持qiankun !!!
    运行时报错
    [import-html-entry]: error occurs while executing entry script http://localhost:40000/src/main.ts?t=1678894335319

    vue create app1-vue
    
    • 1

    在跟目录下添加配置文件 vue.config.js 配置如下:

    module.exports = {
        devServer: {
            port: 10000, // 跟父应用配置的子应用端口一致
            headers: {
                // 因为qiankun内部请求都是fetch来请求资源,所以子应用必须允许跨域
                'Access-Control-Allow-Origin': '*',
            },
        },
        configureWebpack: {
            output: {
                //资源打包路径
                library: 'vueApp',
                libraryTarget: 'umd',
            },
        },
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    配置src/main.js
    父应用之所可以加载子应用,是因为子应用必须暴露三个接口:bootstrap,mount,unmount。

    import Vue from 'vue';
    import App from './App.vue';
    // Vue.config.productionTip = false
    
    let instance = null;
    function render(props) {
        instance = new Vue({
            render: h => h(App),
        }).$mount('#app'); // 这里是挂载到自己的html中  base会拿到这个挂载后的html 将其插入进去
    }
    
    // 动态添加publicPath
    if (window.__POWERED_BY_QIANKUN__) {
        __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    // 默认独立运行
    if (!window.__POWERED_BY_QIANKUN__) {
        render();
    }
    
    // 父应用加载子应用,子应用必须暴露三个接口:bootstrap,mount,unmount
    
    /**
     * required
     * bootstrap z只会在微应用初始化时调用一次,下次微应用重新进入时会直接调用mount钩子,不会再重复触发,bootstrap.
     * 通常我们可以在这里做一些全局变量的初始化,比如不会在unmount 阶段被销毁的应用级别的缓存等。
     *
     */
    
    export async function bootstrap(props) {
        console.log('bootstrap:', props);
    }
    /**
     * required
     * 应用每次进入都会调用mount方法,通常我们在这里触发应用渲染方法
     */
    export async function mount(props) {
        console.log('mount:', props);
        render(props);
    }
    
    /**
     * required
     * 应用每次 切出/卸载 都会调用的方法
     * 通常我们会卸载微应用的应用实例
     */
    export async function unmount(props) {
        console.log('unmount:', props);
        instance.$destroy();
        instance.$el.innerHTML = '';
        instance = null;
    }
    /**
     * not required
     * 仅使用 loadMicroApp 方式加载微应用时生效
     */
    export async function update(props) {
        console.log('update props', props);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    3.创建app2-react 子应用

    npm install -g create-react-app
    create-react-app app2-react
    
    • 1
    • 2

    src/index.js中配置挂载信息,暴露父应三个接口:bootstrap,mount,unmount。

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import { createRoot } from 'react-dom/client';
    import './index.css';
    import App from './App';
    // import reportWebVitals from './reportWebVitals';
    
    // const root = ReactDOM.createRoot(document.getElementById('root'));
    // root.render(
    //   
    //     
    //   
    // );
    
    // 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();
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    function render() {
        root.render(
            <React.StrictMode>
                <App />
            </React.StrictMode>
        );
        // ReactDOM.render(
        //     
        //         
        //     ,
        //     document.getElementById('root')
        // );
    }
    if (!window.__POWERED_BY_QIANKUN__) {
        render();
    }
    
    export async function bootstrap(props) {
        console.log('bootstrap:', props);
    }
    
    export async function mount(props) {
        console.log('mount:', props);
        render();
    }
    
    export async function unmount(props) {
        console.log('unmount:', props);
        // 卸载节点
        // ReactDOM.unmountComponentAtNode(document.getElementById('root'));
        root.unmount();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    项目引入配置依赖react-app-rewired

    npm install react-app-rewired -D
    
    • 1

    在根目录添加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.port = '20000';
                config.headers = {
                    'Access-Control-Allow-Origin': '*',
                };
                return config;
            };
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    package.json中修改启动方式如下:

     "scripts": {
        "start": "PORT=20000 react-app-rewired start",
        "build": "react-app-rewired build",
        "test": "react-app-rewired test",
        "eject": "react-app-rewired eject"
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行项目

    1.运行主项目

    cd base
    npm run serve
    
    • 1
    • 2

    2.运行子应用

    cd app1-vue
    npm run serve
    
    • 1
    • 2

    3.运行子应用

    cd app1-react
    npm run start
    
    • 1
    • 2

    主应用配置的vue子应用的运行端口为10000,react子应用的端口号为20000,三个项目分别启动成功后就可以通过主应用来访问了。这时子应用就会加载到主应用里面。子应用也可以通过各自的端口单独访问。

    在这里插入图片描述
    app1-vue子应用效果
    在这里插入图片描述
    app2-react 子应用效果
    在这里插入图片描述

  • 相关阅读:
    【元胞自动机】元胞自动机短消息网络病毒传播仿真【含Matlab源码 1289期】
    计算机导论真题(一)
    hdu 3549 a flow problem 的多种解法
    蓝桥杯备赛Day8——队列
    珈创生物上市再次失败:先后折戟科创板、创业板,郑从义为董事长
    Centos7.9 一键脚本部署 LibreNMS 网络监控系统
    Ubuntu 安装 Zotero, 并导入原有数据库,然后同步账户
    你是否还迷茫要不要学习Linux?
    Leetcode 242:有效的字母异位词
    预约小程序新选择:强大后端管理功能一览
  • 原文地址:https://blog.csdn.net/baidu_30907803/article/details/126918102