微前端解决的问题

问题:

  • 现代网站越来越复杂:业务功能越来越多,开发人员越来越多
  • 软件工程中,工程问题开始大于软件本身的技术问题
  • 这个时候需要一种解决方案来降低工程的复杂性

理论支撑:

  • 由“康威定律”知系统的复杂度主要取决于系统之间的沟通方式
  • 人越多,沟通越多,协同越多,效率越低,内耗越高
  • 软件组件和模块越多,之间的交互越多,,效率越低,内耗越高
  • 降低内耗的有效方式是减少沟通成本(降低沟通或干脆不沟通)

解决方案:

  • 解耦:后端微服务,前端微前端,微前端是把后端技术范式“微服务”扩展到了前端,目的还是“解耦”
  • 对技术的解耦,本质是对人的解耦。
  • 把一个网站分成多个模块,每个模块由独立的团队完成,各团队之间沟通减少,内部依赖性高,即技术和架构上的“高内聚低耦合”。
  • 同时,各团队之间技术选型可以不一样,每个团队选择适合自己团队的技术框架实现,用最合适的、成本最低的方式实现网站。

补充说明:
当然,任何技术都没有银弹,合适场景下选适合的技术。

微前端落地实现

技术选型

  • 微前端框架:qiankun
  • 开发环境:IDEA(不用vscode)

    环境准备

    ```shell

    准备开发环境

    安装nodejs

    下载地址:http://nodejs.cn/download/

    根据你的操作系统,安装合适的nodejs

    安装完的nodejs默认带包管理器npm

npm设置淘宝镜像源,国内网络环境不好

首先查看当前镜像源

npm config get registry

设置淘宝镜像源

npm config set registry https://registry.npm.taobao.org

需要换回时改为官方的镜像源

npm config set registry https://registry.npmjs.org

不用npm,安装包管理器yarn

npm install -g yarn

集成开发工具

安装IDEA,也安装vscode

  1. <a name="h6th7"></a>
  2. # 创建项目并实践微前端
  3. 我们使用多种技术框架创建多个demo,区分为使用打包工具webpack打包的和不适用webpack打包的
  4. 使用webpack打包的,是使用nodejs现代技术,比如vue、react、angular等框架组件化、模块化开发的<br />不使用webpack打包的,是传统老项目,比如jQuery,HTML,CSS,js开发的静态网站,或者php,JSP等后端渲染技术的网站也归属于这类。
  5. 创建多个项目:
  6. - vue2:vue2框架搭建的demo,绑定端口**8001**
  7. - vue3:vue3框架创建的demo,绑定端口**8002**
  8. - react:react框架创建的demo,绑定端口**8003**
  9. - angular:angular框架创建的demo,绑定端口**8004**
  10. - h5:非webpack打包的,纯html静态网站(代表老旧网站,使用html+css+js写的静态页面,另外php,JSP,jQuery等等也等同),绑定端口**8005**
  11. 然后我们选其中一个demo为主应用,其他demo为子应用,扩展为微前端架构<br />我实验了vue2和react做主应用,其他自己实验吧,举一反三
  12. - 选vue2为主应用,绑定端口**8000**
  13. - 选react为主应用,绑定端口**9000**
  14. <a name="FxVEQ"></a>
  15. ## 创建一个工作区
  16. ```shell
  17. # 创建一个目录作为工作区
  18. mkdir micro-frond-ends-demo
  19. cd micro-frond-ends-demo

创建vue2-demo

  1. ###安装vue-cli
  2. # 文档:https://cli.vuejs.org/zh/guide/installation.html
  3. npm install -g @vue/cli
  4. # 创建vue2-demo
  5. vue create vue2-demo
  6. cd vue2-demo
  7. # 修改绑定端口为8001
  8. # 创建一个与package.json同级的文件vue.config.js并添加如下内容
  9. module.exports = {
  10. devServer: {
  11. port: 8001 //修改为你需要的端口
  12. }
  13. }
  14. # 启动项目
  15. yarn serve

打开: http://localhost:8001/
image.png

创建vue3-demo

  1. # 创建vue3-demo
  2. vue create vue3-demo
  3. cd vue3-demo
  4. # 更改端口绑定为8002
  5. # 创建一个与package.json同级的文件vue.config.js并添加如下内容
  6. module.exports = {
  7. devServer: {
  8. port: 8002 //修改为你需要的端口
  9. }
  10. }
  11. # 启动项目
  12. yarn serve

打开地址: http://localhost:8002/
image.png

创建react-demo

  1. # npx是 npm 5.2+ 附带的 package 运行工具
  2. # 文档:https://react.docschina.org/docs/create-a-new-react-app.html#create-react-app
  3. npx create-react-app my-app
  4. # 修改端口绑定到8003
  5. # package.json的scripts执行前增加环境变量
  6. # windows:set PORT=8003 && react-scripts start
  7. # linux或macos:PORT=4003 react-scripts start
  8. "serve": "set PORT=8003 && react-scripts start",
  9. # 启动项目
  10. yarn serve

打开地址:http://localhost:8003
image.png

创建angualr-demo

  1. # 安装angualr-cli
  2. # 文档:https://angular.cn/cli
  3. npm install -g @angular/cli
  4. #创建项目
  5. ng new angualr-demo
  6. # 修改端口
  7. # package.json文件scripts增加
  8. "serve": "ng serve --port 8004"
  9. # 启动项目
  10. yarn serve

打开地址: http://localhost:8004/
image.png

创建静态h5-demo

创建一个简单的html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>H5静态页面</title>
  6. <script src="//cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
  7. </head>
  8. <body>
  9. <div class="content">
  10. <h1>h5-demo</h1>
  11. 静态HTML页面示例
  12. </div>
  13. <div class="h5-content">
  14. </div>
  15. <style>
  16. .content {
  17. text-align: center;
  18. font-size: large;
  19. padding: 200px 0;
  20. }
  21. </style>
  22. </body>
  23. </html>

为了方便启动,安装一个简单的http-server,然后配置可以跨域访问,新建package.json

  1. {
  2. "name": "h5-demo",
  3. "version": "1.0.0",
  4. "main": "index.html",
  5. "scripts": {
  6. "serve": "cross-env PORT=8005 http-server . --cors",
  7. "test": "echo \"Error: no test specified\" && exit 1"
  8. },
  9. "author": "",
  10. "license": "MIT",
  11. "devDependencies": {
  12. "cross-env": "^7.0.2",
  13. "http-server": "^0.12.1"
  14. }
  15. }

启动项目

  1. # 安装依赖
  2. yarn
  3. #启动项目
  4. yarn serve

访问地址:http://localhost:8005/
image.png

改造为微前端

image.png

我们做一个如上图的主应用,中间content部分替换为子应用

使用布局Layout:
image.png
使用导航菜单Menu:
image.png

主应用使用vue

  1. # 创建一个vue2的项目
  2. vue create main-vue
  3. cd main-vue
  4. # 安装ant desgin vue
  5. yarn add ant-design-vue
  6. ##### 引入ant并创建布局,App.vue改为
  7. <template>
  8. <a-layout id="components-layout-demo-top-side">
  9. <a-layout-header class="header">
  10. <a-menu theme="dark" mode="horizontal">
  11. <a-menu-item key="1"><a href="/home">首页</a></a-menu-item>
  12. <a-menu-item key="2"><a href="/vue2">vue2示例</a></a-menu-item>
  13. <a-menu-item key="3"><a href="/vue3">vue3示例</a></a-menu-item>
  14. <a-menu-item key="4"><a href="/react">react示例</a></a-menu-item>
  15. <a-menu-item key="5"><a href="/angular">angular示例</a></a-menu-item>
  16. <a-menu-item key="6"><a href="/h5">h5示例</a></a-menu-item>
  17. </a-menu>
  18. </a-layout-header>
  19. <a-layout-content>
  20. <a-layout-content class="content">
  21. <div id="subapp">
  22. Content 内容
  23. </div>
  24. </a-layout-content>
  25. </a-layout-content>
  26. <a-layout-footer style="text-align: center;color:white;background-color: #1a1919">
  27. Ant Design ©2018 Created by Ant UED
  28. </a-layout-footer>
  29. </a-layout>
  30. </template>
  31. <style>
  32. #subapp {
  33. text-align: center;
  34. min-height: 800px;
  35. }
  36. </style>
  37. <script>
  38. export default {
  39. name: 'App',
  40. }
  41. </script>
  42. ##### 注册为qiankun主应用
  43. #创建子应用路由数组 apps.js
  44. module.exports = [
  45. {
  46. name: 'vue2-demo',
  47. entry: '//localhost:8001',
  48. container: '#subapp',
  49. activeRule: '/vue2',
  50. },
  51. {
  52. name: 'vue3-demo',
  53. entry: '//localhost:8002',
  54. container: '#subapp',
  55. activeRule: '/vue3',
  56. },
  57. {
  58. name: 'react-demo',
  59. entry: '//localhost:8003',
  60. container: '#subapp',
  61. activeRule: '/react',
  62. },
  63. {
  64. name: 'angular-demo',
  65. entry: '//localhost:8004',
  66. container: '#subapp',
  67. activeRule: '/angular',
  68. },
  69. {
  70. name: 'h5-demo',
  71. entry: '//localhost:8005',
  72. container: '#subapp',
  73. activeRule: '/h5',
  74. },
  75. ]
  76. # main.js注册子应用
  77. import Vue from 'vue'
  78. import App from './App.vue'
  79. import Antd from 'ant-design-vue';
  80. import 'ant-design-vue/dist/antd.css';
  81. // 为 Angular 微应用所做的 zone 包注入
  82. //Angular 运行依赖于 zone.js。
  83. // qiankun 基于 single-spa 实现,single-spa 明确指出一个项目的 zone.js 只能存在一份实例,所以我们在主应用注入 zone.js。
  84. //解决微应用是angualr式, zone.js 的问题
  85. //必须import 'zone.js/dist/zone'
  86. //安装命令:yarn add zone.js
  87. import "zone.js/dist/zone";
  88. import {registerMicroApps, start} from 'qiankun';
  89. import apps from './apps'
  90. Vue.config.productionTip = false
  91. Vue.use(Antd);
  92. new Vue({
  93. render: h => h(App)
  94. }).$mount('#app')
  95. /** 微前端 **/
  96. //注册微应用
  97. registerMicroApps(apps);
  98. //设置默认进入的子应用
  99. // setDefaultMountApp('/h5');
  100. //启动微前端
  101. start();

改造微应用

建议一个一个的改造子应用,主应用中定义子路由的apps.js可以注释一部分,子应用一个一个的注册,调试

微应用vue2-demo

  1. 在 src 目录新增 public-path.js

    1. if (window.__POWERED_BY_QIANKUN__) {
    2. __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    3. }
  2. 入口文件 main.js 修改,为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围。 ```shell // import Vue from ‘vue’ // import App from ‘./App.vue’ // // Vue.config.productionTip = false // // new Vue({ // render: h => h(App), // }).$mount(‘#app’)

import ‘./public-path’; import Vue from ‘vue’; import App from ‘./App.vue’;

Vue.config.productionTip = false;

let instance = null;

function render(props = {}) { const {container} = props; instance = new Vue({ render: (h) => h(App), }).$mount(container ? container.querySelector(‘#app’) : ‘#app’); }

// 独立运行时 if (!window.POWERED_BY_QIANKUN) { render(); }

//qiankun生命周期钩子 export async function bootstrap() { console.log(‘[vue] vue app bootstraped’); }

export async function mount(props) { console.log(‘[vue] vue app mount’); console.log(‘[vue] props from main framework’, props); render(props); }

export async function unmount() { console.log(‘[vue] vue app unmount’); instance.$destroy(); instance.$el.innerHTML = ‘’; instance = null; }

  1. 3. 打包配置修改(vue.config.js
  2. ```shell
  3. const {name} = require('./package.json');
  4. module.exports = {
  5. devServer: {
  6. port: 8001,//修改为你需要的端口
  7. headers: {
  8. 'Access-Control-Allow-Origin': '*',
  9. },
  10. },
  11. configureWebpack: {
  12. output: {
  13. library: `${name}-[name]`,
  14. libraryTarget: 'umd', // 把微应用打包成 umd 库格式
  15. jsonpFunction: `webpackJsonp_${name}`,
  16. },
  17. },
  18. };

微应用vue3-demo

基本跟vue2-demo一致,只是js改成了ts

  1. 在 src 目录新增 public-path.js ```shell if (window.POWERED_BY_QIANKUN) { // eslint-disable-next-line no-undef webpack_public_path = window.INJECTED_PUBLIC_PATH_BY_QIANKUN; }
  1. 2. 入口文件 main.js 修改,为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围。
  2. ```shell
  3. import './public-path';
  4. import {createApp} from 'vue';
  5. import App from './App.vue';
  6. let instance = null;
  7. function render(props = {}) {
  8. const {container} = props;
  9. instance = createApp(App);
  10. instance.mount(container ? container.querySelector('#app') : '#app');
  11. }
  12. if (!window.__POWERED_BY_QIANKUN__) {
  13. render();
  14. }
  15. export async function bootstrap() {
  16. console.log('%c ', 'color: green;', 'vue3.0 app bootstraped');
  17. }
  18. function storeTest(props) {
  19. props.onGlobalStateChange &&
  20. props.onGlobalStateChange(
  21. (value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev),
  22. true,
  23. );
  24. props.setGlobalState &&
  25. props.setGlobalState({
  26. ignore: props.name,
  27. user: {
  28. name: props.name,
  29. },
  30. });
  31. }
  32. export async function mount(props) {
  33. storeTest(props);
  34. render(props);
  35. instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange;
  36. instance.config.globalProperties.$setGlobalState = props.setGlobalState;
  37. }
  38. export async function unmount() {
  39. instance.unmount();
  40. instance._container.innerHTML = '';
  41. instance = null;
  42. }
  1. 打包配置修改(vue.config.js) ```shell const {name} = require(‘./package’);

module.exports = { devServer: { port: 8002, //修改为你需要的端口 headers: { ‘Access-Control-Allow-Origin’: ‘*’, }, }, // 自定义webpack配置 configureWebpack: { output: { // 把子应用打包成 umd 库格式 library: ${name}-[name], libraryTarget: ‘umd’, jsonpFunction: webpackJsonp_${name}, }, }, };

  1. <a name="toks3"></a>
  2. ## 微应用react-demo
  3. 1. 在 src 目录新增 public-path.js
  4. ```shell
  5. if (window.__POWERED_BY_QIANKUN__) {
  6. __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
  7. }
  1. 设置 history 模式路由的 base ```shell
  1. 3. 入口文件 index.js 修改,为了避免根 id #root 与其他的 DOM 冲突,需要限制查找范围。
  2. ```shell
  3. // import React from 'react';
  4. // import ReactDOM from 'react-dom';
  5. // import './index.css';
  6. // import App from './App';
  7. // import reportWebVitals from './reportWebVitals';
  8. //
  9. // ReactDOM.render(
  10. // <React.StrictMode>
  11. // <App />
  12. // </React.StrictMode>,
  13. // document.getElementById('root')
  14. // );
  15. //
  16. // // If you want to start measuring performance in your app, pass a function
  17. // // to log results (for example: reportWebVitals(console.log))
  18. // // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
  19. // reportWebVitals();
  20. import './public-path';
  21. import React from 'react';
  22. import ReactDOM from 'react-dom';
  23. import App from './App';
  24. function render(props) {
  25. const { container } = props;
  26. ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root'));
  27. }
  28. if (!window.__POWERED_BY_QIANKUN__) {
  29. render({});
  30. }
  31. export async function bootstrap() {
  32. console.log('[react16] react app bootstraped');
  33. }
  34. export async function mount(props) {
  35. console.log('[react16] react app mount');
  36. console.log('[react16] props from main framework', props);
  37. render(props);
  38. }
  39. export async function unmount(props) {
  40. console.log('[react16] react app unmount');
  41. const { container } = props;
  42. ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
  43. }
  1. 修改 webpack 配置,安装插件 @rescripts/cli
    1. yarn add @rescripts/cli
    根目录新增 .rescriptsrc.js ```shell const { name } = require(‘./package’);

module.exports = { webpack: (config) => { config.output.library = ${name}-[name]; config.output.libraryTarget = ‘umd’; config.output.jsonpFunction = webpackJsonp_${name}; config.output.globalObject = ‘window’;

  1. return config;
  2. },
  3. devServer: (_) => {
  4. const config = _;
  5. config.headers = {
  6. 'Access-Control-Allow-Origin': '*',
  7. };
  8. config.historyApiFallback = true;
  9. config.hot = false;
  10. config.watchContentBase = false;
  11. config.liveReload = false;
  12. return config;
  13. },

};

  1. 修改 package.json
  2. ```shell
  3. - "start": "react-scripts start",
  4. + "serve": "set PORT=8003 && rescripts start",
  5. - "build": "react-scripts build",
  6. + "build": "rescripts build",
  7. - "test": "react-scripts test",
  8. + "test": "rescripts test",
  9. - "eject": "react-scripts eject"

微应用angular-demo

接入angular较为繁琐,当然这对于选用angualr技术的团队来说,这不算什么。
我这边使用最新版angular,集成顺利

  1. ng version
  2. _ _ ____ _ ___
  3. / \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
  4. / △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
  5. / ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
  6. /_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
  7. |___/
  8. Angular CLI: 13.0.4
  9. Node: 16.13.0
  10. Package Manager: npm 8.1.0
  11. OS: win32 x64
  12. Angular:
  13. ...
  14. Package Version
  15. ------------------------------------------------------
  16. @angular-devkit/architect 0.1300.4 (cli-only)
  17. @angular-devkit/core 13.0.4 (cli-only)
  18. @angular-devkit/schematics 13.0.4 (cli-only)
  19. @schematics/angular 13.0.4 (cli-only)
  1. 在 src 目录新增 public-path.js 文件

    1. if (window.__POWERED_BY_QIANKUN__) {
    2. // eslint-disable-next-line no-undef
    3. __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    4. }
  2. 设置 history 模式路由的 base,src/app/app-routing.module.ts 文件 ```shell

  • import { APP_BASE_HREF } from ‘@angular/common’; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule],
  • // @ts-ignore
  • providers: [{ provide: APPBASEHREF, useValue: window.__POWERED_BY_QIANKUN ? ‘/angular’ : ‘/‘ }] })
  1. 3. 修改入口文件,src/main.ts 文件。
  2. ```shell
  3. // import { enableProdMode } from '@angular/core';
  4. // import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
  5. //
  6. // import { AppModule } from './app/app.module';
  7. // import { environment } from './environments/environment';
  8. //
  9. // if (environment.production) {
  10. // enableProdMode();
  11. // }
  12. //
  13. // platformBrowserDynamic().bootstrapModule(AppModule)
  14. // .catch(err => console.error(err));
  15. import 'public-path';
  16. import { enableProdMode, NgModuleRef } from '@angular/core';
  17. import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
  18. import { AppModule } from './app/app.module';
  19. import { environment } from './environments/environment';
  20. if (environment.production) {
  21. enableProdMode();
  22. }
  23. let app: void | NgModuleRef<AppModule>;
  24. async function render() {
  25. app = await platformBrowserDynamic()
  26. .bootstrapModule(AppModule)
  27. .catch((err) => console.error(err));
  28. }
  29. if (!(window as any).__POWERED_BY_QIANKUN__) {
  30. render();
  31. }
  32. export async function bootstrap(props: Object) {
  33. console.log(props);
  34. }
  35. export async function mount(props: Object) {
  36. render();
  37. }
  38. export async function unmount(props: Object) {
  39. console.log(props);
  40. // @ts-ignore
  41. app.destroy();
  42. }
  1. 修改 webpack 打包配置

先安装 @angular-builders/custom-webpack 插件,注意:angular 9 项目只能安装 9.x 版本,angular 10 项目可以安装最新版

  1. yarn add @angular-builders/custom-webpack

在根目录增加 custom-webpack.config.js ,内容为:

  1. const {name} = require('./package');
  2. module.exports = {
  3. devServer: {
  4. headers: {
  5. 'Access-Control-Allow-Origin': '*',
  6. },
  7. },
  8. output: {
  9. library: `${name}-[name]`,
  10. libraryTarget: 'umd',
  11. //加这行 报错,去掉
  12. // jsonpFunction: `webpackJsonp_${appName}`,
  13. },
  14. };

修改 angular.json,将 [packageName] > architect > build > builder 和 [packageName] > architect > serve > builder 的值改为我们安装的插件,将我们的打包配置文件加入到 [packageName] > architect > build > options。

  1. - "builder": "@angular-devkit/build-angular:browser",
  2. + "builder": "@angular-builders/custom-webpack:browser",
  3. "options": {
  4. + "customWebpackConfig": {
  5. + "path": "./custom-webpack.config.js"
  6. + }
  7. }
  8. - "builder": "@angular-devkit/build-angular:dev-server",
  9. + "builder": "@angular-builders/custom-webpack:dev-server",
  1. 解决 zone.js 的问题

父应用引入 zone.js,需要在 import qiankun 之前引入。

  1. // 为 Angular 微应用所做的 zone 包注入
  2. //Angular 运行依赖于 zone.js。
  3. // qiankun 基于 single-spa 实现,single-spa 明确指出一个项目的 zone.js 只能存在一份实例,所以我们在主应用注入 zone.js。
  4. //解决微应用是angualr式, zone.js 的问题
  5. //必须import 'zone.js/dist/zone'
  6. //安装命令:yarn add zone.js
  7. import "zone.js/dist/zone";

微应用的 src/polyfills.ts 里面的引入 zone.js 代码删掉。

  1. // import 'zone.js'; // Included with Angular CLI.
  1. 修正 ng build 打包报错问题,修改 tsconfig.json 文件,参考issues/431 ```shell
  • “target”: “es2015”,
  • “target”: “es5”,
  • “typeRoots”: [
  • “node_modules/@types”
  • ], ```
  1. 为了防止主应用或其他微应用也为 angular 时, 会冲突的问题,建议给 加上一个唯一的 id,比如说当前应用名称。

src/index.html :

  1. - <app-root></app-root>
  2. + <app-root id="angular9"></app-root>

src/app/app.component.ts :

  1. - selector: 'app-root',
  2. + selector: '#angular9 app-root',

特别说明:angular本身适配了single-spa,如果嫌弃angular接入步骤太繁琐,也可以选择使用 single-spa-angular 插件,参考single-spa-angular 的官网angular demo。我尝试安装没有成功,各种报错。。。遂放弃。

微前端h5-demo

增加一个entry.js入口文件

  1. //声明一个入口文件,用于暴漏相对应的生命周期
  2. //额外创建一个script文件entry用于 export 相对应的 lifecycles
  3. const render = ($) => {
  4. //页面渲染完了 执行操作 比如使用jQuery
  5. $('#h5-content').html('Hello, render with jQuery');
  6. return Promise.resolve();
  7. };
  8. ((global) => {
  9. global['h5-demo'] = {
  10. bootstrap: () => {
  11. console.log('生命周期 bootstrap');
  12. return Promise.resolve();
  13. },
  14. mount: () => {
  15. console.log('生命周期 mount');
  16. return render($);
  17. },
  18. unmount: () => {
  19. console.log('生命周期 unmount');
  20. return Promise.resolve();
  21. },
  22. };
  23. })(window);

index.html增加entry.js

  1. <script src="entry.js" entry></script>

npm管理多个项目

多个项目手工启动麻烦,使用npm管理
在主工作区micro-frond-ends-demo目录创建package.json

  1. cd micro-frond-ends-demo
  2. yarn init
  3. #然后安装npm-run-all 文档:https://github.com/mysticatea/npm-run-all
  4. yarn add npm-run-all --dev
  5. npm-run-all 提供了多种运行多个命令的方式,常用的有以下几个:
  6. --parallel: 并行运行多个命令
  7. --serial: 多个命令按排列顺序执行
  8. --continue-on-error: 是否忽略错误,添加此参数 npm-run-all 会自动退出出错的命令,继续运行正常的
  9. --race: 添加此参数之后,只要有一个命令运行出错,那么 npm-run-all 就会结束掉全部的命令
  10. ### 添加常用脚本
  11. "scripts": {
  12. "all:install": "npm-run-all --serial install:*",
  13. "all:serve": "npm-run-all --parallel serve:*",
  14. "install:vue2-demo": "cd vue2-demo && yarn",
  15. "install:vue3-demo": "cd vue3-demo && yarn",
  16. "install:react-demo": "cd react-demo && yarn",
  17. "install:angular-demo": "cd angular-demo && yarn",
  18. "install:h5-demo": "cd h5-demo && yarn",
  19. "install:main-vue2": "cd main-vue2 && yarn",
  20. "serve:vue2-demo": "cd vue2-demo && yarn serve",
  21. "serve:vue3-demo": "cd vue3-demo && yarn serve",
  22. "serve:react-demo": "cd react-demo && yarn serve",
  23. "serve:angular-demo": "cd angular-demo && yarn serve",
  24. "serve:h5-demo": "cd h5-demo && yarn serve",
  25. "serve:main-vue2": "cd main-vue2 && yarn serve"
  26. }
  27. # 安装全部项目依赖
  28. yarn all:install
  29. # 启动所有项目
  30. yarn all:serve

改造完成 看下效果

首页 http://localhost:8000/home
image.png
vue2示例:http://localhost:8000/vue2
image.png
vue3示例:http://localhost:8000/vue3
image.png
react示例:http://localhost:8000/react
image.png
angular示例:http://localhost:8000/angular
image.png
h5示例:http://localhost:8000/h5
image.png

主应用使用react

布局依旧
image.png

  1. # 创建项目
  2. npx create-react-app main-react
  3. # 修改端口绑定到9000,package.json增加
  4. "serve": "set PORT=8003 && react-scripts start",
  5. # 启动项目
  6. yarn serve
  7. # 启动无问题后改造为微前端主应用
  8. ## 安装ant desgin 设置布局和导航栏
  9. yarn add antd
  10. # App.js改为
  11. import './App.css';
  12. import {Layout, Menu} from 'antd';
  13. const {Header, Content, Footer} = Layout;
  14. function App() {
  15. return (
  16. <div className="App">
  17. <Layout className="layout">
  18. <Header>
  19. <div className="logo"/>
  20. <Menu theme="dark" mode="horizontal">
  21. <Menu.Item key="1"><a href="/home">首页</a></Menu.Item>
  22. <Menu.Item key="2"><a href="/vue2">vue2示例</a></Menu.Item>
  23. <Menu.Item key="3"><a href="/vue3">vue3示例</a></Menu.Item>
  24. <Menu.Item key="4"><a href="/react">react示例</a></Menu.Item>
  25. <Menu.Item key="5"><a href="/angular">angular示例</a></Menu.Item>
  26. <Menu.Item key="6"><a href="/h5">h5示例</a></Menu.Item>
  27. </Menu>
  28. </Header>
  29. <Content style={{padding: '0 50px'}}>
  30. <div id="subapp">
  31. Content 内容
  32. </div>
  33. </Content>
  34. <Footer style={{textAlign: "center",color:"white",backgroundColor: "#1a1919"}}>
  35. Ant Design ©2018 Created by Ant UED
  36. </Footer>
  37. </Layout>
  38. </div>
  39. );
  40. }
  41. export default App;
  42. # 把main-vue中的微应用路由apps.js拿过来
  43. # index.js注册微应用
  44. import React from 'react';
  45. import ReactDOM from 'react-dom';
  46. import './index.css';
  47. import App from './App';
  48. import reportWebVitals from './reportWebVitals';
  49. import apps from './apps'
  50. //解决微应用是angualr式, zone.js 的问题
  51. //必须import 'zone.js/dist/zone'
  52. //安装命令:yarn add zone.js
  53. import 'zone.js/dist/zone';
  54. import {registerMicroApps, start} from "qiankun";
  55. ReactDOM.render(
  56. <React.StrictMode>
  57. <App/>
  58. </React.StrictMode>,
  59. document.getElementById('root')
  60. );
  61. // If you want to start measuring performance in your app, pass a function
  62. // to log results (for example: reportWebVitals(console.log))
  63. // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
  64. reportWebVitals();
  65. //qiankun 微前端
  66. registerMicroApps(apps);
  67. start();

启动看效果,简单截图几个如下:
image.png

image.png

image.png

默认浏览器打开

多项目统一管理,micro-frond-ends-demo下的package.json

  1. {
  2. "name": "micro-frond-ends-demo",
  3. "version": "1.0.0",
  4. "main": "index.js",
  5. "author": "",
  6. "license": "MIT",
  7. "scripts": {
  8. "all:install": "npm-run-all --serial install:*",
  9. "all:serve": "npm-run-all --parallel serve:*",
  10. "install:micro-frond-ends-demo": "yarn",
  11. "install:vue2-demo": "cd vue2-demo && yarn",
  12. "install:vue3-demo": "cd vue3-demo && yarn",
  13. "install:react-demo": "cd react-demo && yarn",
  14. "install:angular-demo": "cd angular-demo && yarn",
  15. "install:h5-demo": "cd h5-demo && yarn",
  16. "install:main-vue2": "cd main-vue2 && yarn",
  17. "install:main-react": "cd main-react && yarn",
  18. "serve:vue2-demo": "cd vue2-demo && yarn serve",
  19. "serve:vue3-demo": "cd vue3-demo && yarn serve",
  20. "serve:react-demo": "cd react-demo && yarn serve",
  21. "serve:angular-demo": "cd angular-demo && yarn serve",
  22. "serve:h5-demo": "cd h5-demo && yarn serve",
  23. "serve:main-vue2": "cd main-vue2 && yarn serve",
  24. "serve:main-react": "cd main-react && yarn serve"
  25. },
  26. "devDependencies": {
  27. "npm-run-all": "^4.1.5"
  28. }
  29. }

应用启动后的默认动作

  1. ### react默认启动后会打开浏览器,关闭打开行为
  2. # 通过环境变量方式,在react项目根目录创建文件.env
  3. #关闭自动打开浏览器
  4. BROWSER=none
  5. #绑定端口
  6. PORT=9000
  7. ### vue默认启动后不打开浏览器,设置为打开
  8. # 通过修改vue.config.js文件配置
  9. module.exports = {
  10. devServer: {
  11. //绑定端口
  12. port: 8000,
  13. //默认打开浏览器
  14. open: true,
  15. }
  16. }
  17. ### angular通过命令行可以直接修改
  18. ng serve --port 8004 --open

本项目设置为:启动所有项目,然后浏览器打开main-vue项目

源代码

https://gitee.com/dakuohao/micro-frond-ends-demo

(完)