• vue3+qiankun+Typescript开发微前端项目


    路由为hash模式

    一、主应用配置

    1、安装 qiankun
    yarn add qiankun 或者 npm i qiankun -S
    
    2、注册微应用并启动
    import { registerMicroApps, start } from 'qiankun';
    
    const isDev = process.env.NODE_ENV === "development";
    const apps = [
      {
        name: 'FirstMicroApp',
        entry: isDev ? '//localhost:8082' : '/first-micro-app/',
        container: '#microAppContainer',
        activeRule: '#/exhibitionHall',
      },
    ];
    registerMicroApps(apps, {
      beforeLoad: [
        // app => {
        //   console.log("before load", app);
        // }
      ],
      beforeMount: [
        // app => {
        //   console.log("before mount", app);
        // }
      ],
      afterUnmount: [
        // app => {
        //   console.log("after unMount", app);
        // }
      ],
    });
    
    // 启动 qiankun
    start();
    

    二、微应用配置

    1、在 src 目录新增 public-path.js
    /* eslint-disable */
    if (window.__POWERED_BY_QIANKUN__) {
      __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    
    2、入口文件 main.ts 修改
    import './public-path';
    import { createApp } from 'vue';
    import Antd from 'ant-design-vue';
    import App from './App.vue';
    import router from './router';
    import store from './store';
    import 'ant-design-vue/dist/antd.css';
    
    // createApp(App).use(store).use(router).mount('#app');
    
    type Props = {
      container?: HTMLElement;
    }
    
    let app: any = null;
    
    function render(props: Props = {}) {
      const { container } = props;
      app = createApp(App);
      app.use(router);
      app.use(store);
      app.use(Antd);
      app.mount(container ? container.querySelector('#app') : '#app');
    }
    
    // 独立运行时
    // eslint-disable-next-line
    if (!window.__POWERED_BY_QIANKUN__) {
      render();
    }
    
    export async function bootstrap(): Promise<void> {
      console.log('vue app bootstraped');
    }
    
    export async function mount(props: Props): Promise<void> {
      render(props);
    
      // 注册全局主应用路由 mainRouter
      // const { mainRouter } = props;
      // app.config.globalProperties.$mainRouter = mainRouter;
    }
    
    export async function unmount(): Promise<void> {
      app.unmount();
      app = null;
    }
    
    3、打包配置 vue.config.js 修改
    const { name } = require('./package.json');
    const port = 8082;
    const isDev = process.env.NODE_ENV === 'development';
    
    module.exports = {
      publicPath: dev ? '/' : '/first-micro-app/',
      devServer: {
        port,
        headers: {
          'Access-Control-Allow-Origin': '*',
        },
      },
      configureWebpack: {
        output: {
          library: `${name}-[name]`,
          libraryTarget: 'umd',
          jsonpFunction: `webpackJsonp_${name}`,
        },
      },
    }
    

    三、批量打包

    1、在主应用与微应用同级新增 package.json 文件
    {
      "name": "qiankun-vue3",
      "version": "1.0.0",
      "private": true,
      "scripts": {
        "install:all": "node scripts/index.js --serial install",
        "serve:all": "node scripts/index.js --parallel serve",
        "build:all": "yarn run buildApp && yarn run cpApp",
        "buildApp": "node scripts/index.js --serial build",
        "cpApp": "node scripts/build.js" 
      }
    }
    
    2、在主应用与微应用同级新增 scripts 文件夹
    2.1 批量 install、serve、build

    scripts 文件夹中新增 spawn.js 文件

    const crossSpawn = require("child_process").spawn;
    
    function kill() {
      const platform = process.platform;
      if (platform === "win32") {
        crossSpawn("taskkill", ["/F", "/T", "/PID", this.pid]);
      } else {
        crossSpawn.kill(this.pid);
      }
    }
     
    module.exports = function spawn(command, args, options) {
      const child = crossSpawn(command, args, options);
      child.kill = kill;
    
      return child;
    }
    

    scripts 文件夹中新增 index.js 文件

    /**
     * argv[1]为 --parallel / --serial
     * argv[2]为 install / serve / build
     */
    const path = require('path');
    const fs = require('fs');
    const spawn = require('./spawn');
    
    const entry = path.resolve();
    const microApps = fs.readdirSync(entry).filter(app => /app$/.test(app));
    
    const remove = (array, x) => {
      const index = array.indexOf(x);
      if (index > -1) {
        array.splice(index, 1);
      }
    };
    
    const runApp = (app, task) => {
      let cp = null;
      const promise = new Promise((resolve, reject) => {
        const command = 'npm';
        const args = [];
        if (task === 'install') {
          args.push(task);
        } else {
          args.push('run', task);
        }
        
        const { stdin, stdout, stderr } = process;
        const options = {
          stdio: [stdin, stdout, stderr],
        };
        if (app) {
          options.cwd = path.resolve(app);
        }
    
        cp = spawn(command, args, options);
    
        cp.on("error", (err) => {
          cp = null;
          reject(err);
        });
    
        cp.on("close", (code) => {
          cp = null;
          resolve({ app, code });
        });
      });
    
      // kill pid
      promise.abort = () => {
        if (cp) {
          cp.kill();
          cp = null;
        }
      };
    
      return promise;
    };
    
    const runApps = (apps) => {
      return new Promise((resolve, reject) => {
        if (apps.length === 0) {
          resolve([]);
          return;
        }
    
        const results = apps.map(app => ({ name: app, code: undefined }))
        const queue = apps.map((app, index) => ({ name: app, index }))
        const promises = [];
        let error = null;
        let aborted = false;
    
        const done = () => {
          if (error) {
            reject(error);
          }
          resolve(results);
        };
    
        const abort = () => {
          if (aborted) {
            return;
          }
          aborted = true;
          if (promises.length) {
            for (const p of promises) {
              p.abort();
            }
            Promise.all(promises).then(done, reject);
          } else {
            done();
          }
        };
        
        const next = (task) => {
          if (aborted) {
            return;
          }
          if (!queue.length) {
            if (!promises.length) {
              done();
            }
            return
          }
    
          const app = queue.shift();
          const promise = runApp(app.name, task);
          promises.push(promise);
          promise.then(
            result => {
              remove(promises, promise);
              
              if (aborted) {
                return;
              }
    
              results[app.index].code = result.code
              
              if (result.code) {
                error = {
                  name: result.app,
                  code: result.code,
                  results: results,
                };
                abort();
                return;
              }
    
              next(task);
            },
            err => {
              remove(promises, promise);
              error = err;
              abort();
              return;
            },
          );
        };
    
        const [mode, task = ''] = process.argv.slice(2);
        
        if (!['--parallel', '--serial'].includes(mode)) {
          const error = 'process.argv第三个参数只能为--parallel、--serial其中之一';
          return reject(error);
        }
        if (!['install', 'serve', 'build'].includes(task)) {
          const error = 'process.argv第四个参数只能为install、serve、build其中之一';
          return reject(error); 
        }
        const len = mode === '--parallel' ? apps.length : 1;
        for (let i = 0; i < len; i++) {
          next(task);
        }
      });
    };
    
    // console.log('microApps', microApps);
    runApps(microApps)
      .then(result => {
        // console.log('ok');
      })
      .catch(error => {
        console.error('error: ', error);
      });
    
    2.2 批量拷贝 build 后的 dist 文件夹到 外层 dist 目录
    /**
     * build
     * 先build到各自文件夹下的dist目录,再拷贝到最外层dist目录
     * 1、先删除上次的dist目录 rm -rf dist
     * 2、拷贝主应用dist到最外层dist cp -rf 20f6e232--cloud--FirstMainMicroApp/dist dist
     * 3、拷贝微应用dist到最外层dist中,且改名为对应微应用名称 cp -rf 20f6e232--cloud--FirstSubMicroApp/dist dist/FirstSubMicroApp
     */
    // const { resolve, resolve: pathResolve } = require('path');
    const { resolve: pathResolve } = require('path');
    const { access, constants, readdirSync } = require('fs');
    const { spawn } = require('child_process');
    
    const entry = pathResolve();
    // const microApps = readdirSync(entry).filter(app => /app$/.test(app));
    const mainAppFolderName = 'main-app';
    const microAppFolderName = 'micro-app';
    const appNameRegex = new RegExp(`(${mainAppFolderName}|${microAppFolderName})$`)
    console.log('appNameRegex', appNameRegex);
    const microApps = readdirSync(entry).filter(app => appNameRegex.test(app));
    console.log('microApps', appNameRegex, microApps);
    
    // 文件夹是否存在
    const isExist = (dir) => {
      return new Promise((resolve, reject) => {
        access(dir, constants.F_OK, err => {
          // err为null时表示存在文件夹dir
          console.log('isExist', err);
          resolve(err);
        })
      })
    };
    
    // 删除最外层目录下文件夹
    const removeDir = (dir) => {
      return new Promise((resolve, reject) => {
        console.log('removedir', dir);
        const cp = spawn('rm', ['-rf', dir], { cwd: entry });
    
        cp.on('error', err => {
          console.error(`removeDir err: ${err}`);
          reject(err);
        });
    
        cp.on('close', code => {
          // 此时code为0
          console.log(`removeDir exited with code ${code}`);
          resolve({ code });
        });
      });
    };
    
    // 拷贝文件夹
    // cp -rf 20f6e232--cloud--FirstMainMicroApp/dist dist
    // cp -rf 20f6e232--cloud--FirstSubMicroApp/dist dist/FirstSubMicroApp
    const copyDir = (src, dst) => {
      return new Promise((resolve, reject) => {
        const cp = spawn('cp', ['-rf', src, dst], { cwd: entry });
    
        cp.on('error', err => {
          reject(err);
        });
    
        cp.on('close', code => {
          // 此时code为0
          resolve({ code });
        });
      })
    };
    
    // 先拷贝主应用dist到最外层目录
    const copyMainDir = (dist) => {
      return new Promise((resolve, reject) => {
        const mainApp = microApps.find(app => app.includes(mainAppFolderName));
        const src = pathResolve(mainApp, dist);
        copyDir(src, dist)
          .then(res => resolve(res))
          .catch(err => reject(err));
      });
    };
    
    // 再拷贝微应用dist到主应用dist中, 且给微应用dist重命名
    const copySubDir = (dist) => {
      const promises = [];
      const subApps = microApps.filter(app => app.includes(microAppFolderName));
      console.log('subApps: ', subApps);
      subApps.forEach(app => {
        let rename = app;
        if (app.includes('--')) {
          const appNames = app.split('--');
          const len = appNames.length;
          rename = appNames[len - 1];
        }
        const src = pathResolve(app, dist);
        const dst = pathResolve(dist, rename);
        const promise = copyDir(src, dst);
        promises.push(promise);
      });
    
      return promises;
    };
    
    // 拷贝主应用与微应用所有dist目录
    const copyDirs = (dir) => {
      copyMainDir(dir)
        .then(res => {
          Promise.all(copySubDir(dir))
            .then(res => {
              console.log('复制dist目录成功');
            })
            .catch(err => { 
              console.log('复制微应用dist目录失败', err);
            });
        })
        .catch(err => {
          console.log('复制主应用dist目录失败', err);
        });
    };
    
    const buildMicroApps = async (dir) => {
      try {
        const isNull = await isExist(pathResolve(dir));
        if (!isNull) {
          const removeRes = await removeDir(dir);
          console.log('removeRes', removeRes);
          if (!removeRes.code) {
            copyDirs(dir);
          }
        } else {
          copyDirs(dir);
        }
      } catch(err) {
        console.log(err);
      }
    };
     
    buildMicroApps('dist');
    
    3、参考文件npm-run-all
  • 相关阅读:
    django入门系列 -表单视图
    django学习 -模型层初体验
    python 元组与list的区别
    解决容器内部无法访问宿主机端口的问题
    记一次pyccharm Terminal django 执行 python migrate 无法生成新的数据表的报错过程
    ES学习之ES语法入门
    docker seleniumgrid 问题解决
    selenium的Grid方式遇到 1 requests waiting for a slot to be free. 问题
    js写的ajax
    ajax jQ写的上传进度条
  • 原文地址:https://www.cnblogs.com/xsnow/p/15895082.html
Copyright © 2020-2023  润新知