Giter Site home page Giter Site logo

micro-front-demo's Introduction

一、前言

文章地址

大型中后台项目一般包括10个以上的子项目,如果维护在一个单页面应用中,项目就会越来越大,而且不利于版本的迭代,微前端就很好的解决了这些问题。

这篇文章主要来体验下蚂蚁的微前端:qiankun,虽然比较成熟了,但在体验过程中还是有一些问题,记录总结下,项目代码

实践项目以react单页面应用为主应用,然后构建了三个微应用:react、vue3、node静态页面

启动项目


git clone

cd micro-front-demo

yarn install

yarn start

访问地址:http://localhost:10000

二、前期准备

微前端要求多个前端服务,所以我们先准备几个应用,使用不同的技术栈,体验微前端的强大

1、项目架构

mirco-front-demo作为整个服务的根目录,为了便于实践,主应用和微应用将放在一起。

主应用:

  • my-app
    • port: 10000
    • create-react-app

微应用:

  • micro-reat-1
    • port: 10100
    • create-react-app
  • micro-vue-2
    • port: 10200
    • vue3
  • micro-static-3
    • port: 10300
    • node + html

2、主应用my-app

需要提前安装create-react-app:sudo npx install create-react-app

通过create-react-app创建主应用my-app,其他的微应用都会挂载到主应用。

# 在根目录
mkdir mirco-front-demo
cd mirco-front-demo
# 新建主应用my-app
npx create-react-app my-app
cd my-app
# 通过.env文件修改启动端口
echo "PORT=10000" > .env
yarn start

3、微应用micro-react-1

cd micro-front-demo
npx create-react-app micro-react-1

同主应用一样创建一个React应用,命名micro-react-1,并修改启动端口号(也可以使用.env文件修改)

修改启动端口号:

{
    "scripts": {
        "start": "PORT=10100 react-app-rewired start",
        "build": "react-app-rewired build",
        "test": "react-app-rewired test",
        "eject": "react-app-rewired eject"
    }
}

4、微应用micro-vue-2

使用vue3.0,提前安装vue-cli:yarn global add @vue/cli官方文档

vue create micro-vue-2
cd micro-vue-2
touch vue.config.js

除了微应用1和微应用2修改启动端口号的方法,这里也可以对webpack进行覆盖

vue-config.js

const { defineConfig } = require('@vue/cli-service')

module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    // 监听端口
    port: 10200,
    // 配置跨域请求头,解决开发环境的跨域问题
    headers: {
      "Access-Control-Allow-Origin": "*",
    }
  }
}

启动微应用yarn serve

5、微应用micro-static-3

# 新建微应用项目3
mkdir micro-static-3
cd micro-static-3
npm init
yarn add express cors

# 新建项目文件
mkdir static
touch index.js

cd static
touch index.html

index.js

const express = require('express')
const cors = require('cors')
const app = express()
const port = 10300

app.use(cors())
app.use(express.static('static'));

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

启动微应用node index.js

6、同时启动微应用

cd mirco-front-demo
npm init
yarn add npm-run-all -D

修改当前目录下package.json

{
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "npm-run-all --parallel start:*",
        "start:main": "cd my-app && yarn start",
        "start:micro-react": "cd micro-react-1 && yarn start",
        "start:micro-vue": "cd micro-vue-2 && yarn serve",
        "start:micro-static": "cd micro-static-3 && node index.js"
    }
}

执行命令

cd micro-front-demo
yarn start

这样就同时启动了四个前端服务

三、配置qiankun

1、主应用路由和样式

改造下主应用样式,整个顶部导航栏和侧边栏属于主应用,而中间空白的部分可以展示主应用或子应用的页面。

主应用路由安装react-router-dom,通过history模式渲染。

image.png

在侧边栏点击不同的链接会加载不同的子应用,样式和路由具体可以看示例代码

2、注册微应用

修改单页面应用渲染根节点rootmain-root,防止和微应用中渲染节点冲突。

image.png

并且增加一个通过id标记的DIV,用来嵌入微应用,接着引入qiankun,这里id=subApp用来挂载微应用

yarn add qiankun 

image.png

src/index.js中配置

import React from 'react';
import ReactDOM from 'react-dom';
import { registerMicroApps, start } from 'qiankun';
import './index.css';
import App from './App';

function render(){
  ReactDOM.render(<App />, document.querySelector('#main-root'));
}

render({});

registerMicroApps([
  {
    name: 'react', // app name registered
    entry: '//localhost:10100',
    container: "#subApp",
    activeRule: '/react'
  },
  {
    name: 'vue', // app name registered
    entry: '//localhost:10200',
    container: "#subApp",
    activeRule: '/vue'
  },
  {
    name: 'static', // app name registered
    entry: '//localhost:10300',
    container: "#subApp",
    activeRule: '/static'
  }
], {
  beforeLoad: app => {
    console.log('before load app.name=====>>>>>', app.name)
  },
  beforeMount: [
    app => {
      console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name)
    }
  ],
  afterMount: [
    app => {
      console.log('[LifeCycle] after mount %c%s', 'color: green;', app.name)
    }
  ],
  afterUnmount: [
    app => {
      console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name)
    }
  ]
})

start()

3、微应用配置

1) micro-react-1

引入react-router-dom给微应用配置路由,展示不同的页面,如下:

image.png

修改src/index.js下启动文件

function render(props) {
  const { container } = props;
  ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root'));
}

if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}

export async function bootstrap() {
  console.log('[react16] react app bootstraped');
}

export async function mount(props) {
  console.log('[react16] props from main framework', props);
  render(props);
}

export async function unmount(props) {
  const { container } = props;
  ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
}

给微应用添加导出声明周期函数,微应用挂载成功时,渲染到当前应用的root节点。

当前cra并没有释放webpack配置,所以要通过插件覆盖配置:

yarn add react-app-rewired -D
"scripts": {
    "start": "PORT=10100 react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
}

在当前微应用根目录下touch config-overrides.js

const { name } = require('./package');

module.exports = {
    webpack: (config, env) => {
        config.output.library = `${name}-[name]`;
        config.output.libraryTarget = 'umd';
        config.output.globalObject = 'window';
        config.output.chunkLoadingGlobal = `webpackJsonp_${name}`;
        return config;
    },
    devServer: (_) => {
        const config = _;
        config.headers = {
          'Access-Control-Allow-Origin': '*',
        };
        config.historyApiFallback = true;
        config.hot = false;
        config.watchContentBase = false;
        config.liveReload = false;
        config.injectClient = false
        return config;
    }
}

启动微应用yarn start,正常运行

2) micro-vue-2

新增 public-path.js 文件,用于修改运行时的 publicPath

src/public-path.js

if (window.__POWERED_BY_QIANKUN__) {
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

引入vue路由,设置成history模式,baseRouter设置成vue,导出声明周期函数

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './public-path'

const app = createApp(App);

function render(props) {
  const { container } = props;
  app.use(router)
  .mount(container ? container.querySelector('#app') : '#app')
}

export async function bootstrap() {
    console.log('bootstrap');
}

export async function mount(props) {
    console.log('mount', props);
    render(props);
}

export async function unmount() {
    console.log('unmount');
    app.unmount();
}

修改webpack配置vue.config.js

module.exports = defineConfig({
    configureWebpack: {
        output: {
          // 微应用的包名,这里与主应用中注册的微应用名称一致
          library: name,
          // 将你的 library 暴露为所有的模块定义下都可运行的方式
          libraryTarget: "umd",
          // 按需加载相关,设置为 webpackJsonp_微应用名称 即可
          chunkLoadingGlobal: `webpackJsonp_${name}`,
        }
    }
})

启动应用yarn serve

3) micro-static-3

这是一个express服务启动的静态服务

文件入口导出声明周期entry.js

const render = ($) => {
    $('#app').html('Hello, render html, 一个通过http服务部署的静态网站');
    return Promise.resolve();
};

((global) => {
    global['static'] = {
      bootstrap: () => {
        console.log('purehtml bootstrap');
        return Promise.resolve();
      },
      mount: () => {
        console.log('purehtml mount');
        return render($);
      },
      unmount: () => {
        console.log('purehtml unmount');
        return Promise.resolve();
      },
    };
})(window);

然后在模版文件导入

image.png

其实也是挂载在了app节点。

启动当前微服务node index.js

4、启动所有应用

cd micro-front-demo
yarn start

四、报错处理

1、报错信息

'__webpack_public_path__' is not defined

Uncaught Error: single-spa minified message #20

覆盖CRA的Webpack配置

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';
    return config;
  },


  devServer: (_) => {
    const config = _;
    config.headers = {
      'Access-Control-Allow-Origin': '*',
    };
    config.historyApiFallback = true;
    config.hot = false;
    config.watchContentBase = false;
    config.liveReload = false;
    return config;
  },
};

这里报错,是因为是webpack5.x不兼容config.output.jsonpFunction的写法,需要替换成config.output.chunkLoadingGlobal

2、报错信息

construct.js:17 Uncaught Error: application 'react' died in status LOADING_SOURCE_CODE: [qiankun]: You need to export lifecycle functions in react entry

没有将生命周期暴露出来,需要挨个检查下面的配置

Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
 - configuration.output has an unknown property 'jsonpFunction'. These properties are valid:
   object { assetModuleFilename?, asyncChunks?, auxiliaryComment?, charset?, chunkFilename?, chunkFormat?, chunkLoadTimeout?, chunkLoading?, chunkLoadingGlobal?, clean?, compareBeforeEmit?, crossOriginLoading?, cssChunkFilename?, cssFilename?, devtoolFallbackModuleFilenameTemplate?, devtoolModuleFilenameTemplate?, devtoolNamespace?, enabledChunkLoadingTypes?, enabledLibraryTypes?, enabledWasmLoadingTypes?, environment?, filename?, globalObject?, hashDigest?, hashDigestLength?, hashFunction?, hashSalt?, hotUpdateChunkFilename?, hotUpdateGlobal?, hotUpdateMainFilename?, iife?, importFunctionName?, importMetaName?, library?, libraryExport?, libraryTarget?, module?, path?, pathinfo?, publicPath?, scriptType?, sourceMapFilename?, sourcePrefix?, strictModuleErrorHandling?, strictModuleExceptionHandling?, trustedTypes?, umdNamedDefine?, uniqueName?, wasmLoading?, webassemblyModuleFilename?, workerChunkLoading?, workerWasmLoading? }
   -> Options affecting the output of the compilation. `output` options tell webpack how to write the compiled files to disk.
   Did you mean output.chunkLoadingGlobal (BREAKING CHANGE since webpack 5)?

micro-front-demo's People

Contributors

richlpf avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.