Micro front end - qiankun configuration method

What is micro frontend

Micro front-end refers to the micro-services that exist in the browser. It draws on the architectural concept of micro-services and extends the concept of micro-services to the front-end.

If you are unfamiliar with the concept of microservices, you can simply understand that micro front-end is to split a large front-end application into multiple modules. Each micro front-end module can be managed by different teams and can choose the framework independently. It also has its own warehouse and can be deployed and launched independently.

Generally speaking, micro frontends are mostly used in mid- and back-end projects in enterprises, because mid- and back-end projects within enterprises have a relatively long survival time, often three to five years or more, and the probability of eventually evolving into a monolithic application is often higher than other types. web application. This brings about two problems : backward technology stack and slow compilation and deployment .

Let’s take a common e-commerce platform as an example. The back-end management system of a certain e-commerce platform that has survived for 10 years consists of several modules, including commodity management, inventory management, logistics management and other modules. However, due to historical reasons, this project has not been completed for a long time. I started writing it with jquery, because based on the principle that it can run, the business has not changed much in the past 10 years, so it has continued. To this day, it is still maintained with jquery.

There is a good solution that does not require jquery to accumulate code on the original project, but takes out the new project to write separately and achieve independent deployment.

  • Previous project structure

  • After introducing micro front-end

Benefits of Micro Frontends

Team autonomy

In the company, teams are generally divided according to business. When there is no micro front end, if several teams maintain a project, they will definitely encounter some conflicts, such as conflicts in merging codes, conflicts in launch time, etc. After applying the micro front end, the project can be split into several small modules according to the business modules. Each module is maintained by a different team, developed separately, and deployed online separately. In this way, the team can directly achieve autonomy and reduce or even There will be no conflicts with other teams.

Compatible with old projects

If your company has the ancient jquery or other monolithic projects in the story, but you don’t want to use the old technology stack to maintain them, it is a good choice to use micro-frontends to split the projects.

Across technology stacks

According to our example above, if we need to add a new business module to our micro front-end system, we only need to create a separate project. As for what technology stack the project uses, it can be defined by the team itself, even if it is used with other modules. There will be no problems with different technology stacks

Summarize

Existing micro front-end solutions

iframe

Everyone is familiar with iframe. If implemented through iframe, each child application is embedded into the parent application through the iframe tag. iframe has natural isolation properties. Each child application and between the child application and the parent application can be mutually exclusive. Influence.

But iframe also has many disadvantages:

  1. The url is out of sync, and if the page is refreshed, the route to the page in the iframe will be lost.
  2. The global context is completely isolated and memory variables are not shared.
  3. The UI is not synchronized. For example, if a page in an iframe has a pop-up component with a mask layer, the mask cannot cover the entire browser and can only take effect in the iframe.
  4. slow. Each time a sub-application is entered, it is a process of rebuilding the browser context and reloading resources.
single-spa

Official website: https://zh-hans.single-spa.js.org/docs/getting-started-overview

single-spa is the earliest micro front-end framework and is compatible with many technology stacks.

single-spa first registers the routes of all sub-applications in the base. When the URL changes, it will be matched. Which sub-application is matched will load the corresponding sub-application.

Compared with the iframe implementation, single-spa shares a global context between the base and each sub-application, and there is no URL desynchronization or UI desynchronization. However, single-spa also has the following shortcomings:

  1. js isolation and css isolation are not implemented
  2. A lot of configuration needs to be modified, including the base and sub-applications, and it cannot be used out of the box.
qiankun

qiankun is a micro-front-end framework open sourced by Alibaba. It has been fully tested and polished by a number of online applications within Alibaba, so it can be used with confidence. What are the advantages of qiankun?

  • Based on single-spa packaging, providing a more out-of-the-box API
  • It has nothing to do with the technology stack. Applications from any technology stack can be used/accessed, whether it is React/Vue/Angular/JQuery or other frameworks.
  • Access via HTML Entry, as easy as using an iframe
  • Realizes style isolation and js isolation that single-spa does not have
  • Resource preloading: preload unopened micro-application resources during the browser's idle time to speed up the micro-application opening speed.

Practical implementation of micro front-end based on qiankun

We chose qiankun for practical development in this course. The project directory is as follows:

├── micro-base     // 基座
├── sub-react       // react子应用,create-react-app创建的react应用,使用webpack打包
├── sub-vue  // vue子应用,vite创建的子应用
  • Base (main application): Mainly responsible for integrating all sub-applications, providing an entrance to access the display of the sub-applications you need, and trying not to write complex business logic
  • Sub-applications: modules divided according to different businesses. Each sub-application is packaged into umda module for the base (main application) to load.
base

The base is a project built create-react-appwith scaffolding and antdcomponent libraries. You can also choose Vue or other frameworks. Generally speaking, the base only provides a container for loading sub-applications, and tries not to write complex business logic.

Base modification
  1. Install qiankun
// 安装qiankun
npm i qiankun // 或者 yarn add qiankun
  1. Modify entry file
// 在src/index.tsx中增加如下代码
import {
    
     start, registerMicroApps } from 'qiankun';

// 1. 要加载的子应用列表
const apps = [
  {
    
    
    name: "sub-react", // 子应用的名称
    entry: '//localhost:8080', // 默认会加载这个路径下的html,解析里面的js
    activeRule: "/sub-react", // 匹配的路由
    container: "#sub-app" // 加载的容器
  },
]

// 2. 注册子应用
registerMicroApps(apps, {
    
    
  beforeLoad: [async app => console.log('before load', app.name)],
  beforeMount: [async app => console.log('before mount', app.name)],
  afterMount: [async app => console.log('after mount', app.name)],
})

start() // 3. 启动微服务

After the micro-application information is registered, once the browser URL changes, qiankun's matching logic will be automatically triggered. All micro-applications that match activeRule rules will be inserted into the specified container, and the life cycle hooks exposed by the micro-applications will be called in sequence.

  • registerMicroApps(apps, lifeCycles?)

    Register all sub-applications, qiankun will match the corresponding sub-applications according to activeRule and load them

  • start(options?)

    Start qiankun, you can preload and sandbox settings

At this point, the base transformation is completed. If it is an old project or a project with other frameworks and you want to change it to a micro front end, the method is similar.

react sub-application

Create and configure using create-react-appscaffolding webpack. In order not to eject all webpack configurations, we choose to use react-app-rewiredtools to transform webpack configurations.

  1. Modify the entry file of the sub-application
let root: Root

// 将render方法用函数包裹,供后续主应用与独立运行调用
function render(props: any) {
    
    
  const {
    
     container } = props
  const dom = container ? container.querySelector('#root') : document.getElementById('root')
  root = createRoot(dom)
  root.render(
    <BrowserRouter basename='/sub-react'>
      <App/>
    </BrowserRouter>
  )
}

// 判断是否在qiankun环境下,非qiankun环境下独立运行
if (!(window as any).__POWERED_BY_QIANKUN__) {
    
    
  render({
    
    });
}

// 各个生命周期
// bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
export async function bootstrap() {
    
    
  console.log('react app bootstraped');
}

// 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
export async function mount(props: any) {
    
    
  render(props);
}

// 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
export async function unmount(props: any) {
    
    
  root.unmount();
}
  1. Added public-path.js
if (window.__POWERED_BY_QIANKUN__) {
    
    
  // 动态设置 webpack publicPath,防止资源加载出错
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
  1. Modify webpack configuration file
// 在根目录下新增config-overrides.js文件并新增如下配置
const {
    
     name } = require("./package");

module.exports = {
    
    
  webpack: (config) => {
    
    
    config.output.library = `${
      
      name}-[name]`;
    config.output.libraryTarget = "umd";
    config.output.chunkLoadingGlobal = `webpackJsonp_${
      
      name}`;
    return config;
  }
};
vue child application
Create sub-application
# 创建子应用,选择vue3+vite
npm create vite@latest
Modify sub-application
  1. Install vite-plugin-qiankundependency packages
npm i vite-plugin-qiankun # yarn add vite-plugin-qiankun
  1. Modify vite.config.js
import qiankun from 'vite-plugin-qiankun';

defineConfig({
    
    
    base: '/sub-vue', // 和基座中配置的activeRule一致
    server: {
    
    
      port: 3002,
      cors: true,
      origin: 'http://localhost:3002'
    },
    plugins: [
      vue(),
      qiankun('sub-vue', {
    
     // 配置qiankun插件
        useDevMode: true
      })
    ]
})
  1. Modify main.ts
import {
    
     createApp } from 'vue'
import './style.css'
import App from './App.vue'
import {
    
     renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';

let app: any;
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
    
    
  createApp(App).mount('#app');
} else {
    
    
  renderWithQiankun({
    
    
    // 子应用挂载
    mount(props) {
    
    
      app = createApp(App);
      app.mount(props.container.querySelector('#app'));
    },
    // 只有子应用第一次加载会触发
    bootstrap() {
    
    
      console.log('vue app bootstrap');
    },
    // 更新
    update() {
    
    
      console.log('vue app update');
    },
    // 卸载
    unmount() {
    
    
      console.log('vue app unmount');
      app?.unmount();
    }
  });
}

Replenish

style isolation

qiankun implements style isolation between sub-applications, but the style isolation between the base and sub-applications is not implemented, so the previous styles of the base and sub-applications will still conflict and overwrite.

Solution:

  • Each applied style uses a fixed format
  • css-moduleAutomatically prefix each application by
Jump between sub-applications
  • The main application and micro-application are both hashmodes. The main application hashjudges the micro-application based on , so there is no need to consider this issue.
  • historyIn this mode, it is not possible to jump between micro-applications, or to jump to the main application page from a micro-application, by directly using the routing instance of the micro-application. The reason is that the routing instance jumps of micro-applications are all based on routing base. There are two ways to jump:
    1. history.pushState()
    2. Pass the routing instance of the main application to propsthe micro application, and the routing instance of the micro application will jump.

Specific plan: Copy and monitor the method in the base history.pushState()and make corresponding jump logic

// 重写函数
const _wr = function (type: string) {
    
    
  const orig = (window as any).history[type]
  return function () {
    
    
    const rv = orig.apply(this, arguments)
    const e: any = new Event(type)
    e.arguments = arguments
    window.dispatchEvent(e)
    return rv
  }
}

window.history.pushState = _wr('pushState')

// 在这个函数中做跳转后的逻辑
const bindHistory = () => {
    
    
  const currentPath = window.location.pathname;
  setSelectedPath(
  	routes.find(item => currentPath.includes(item.key))?.key || ''
  )
}

// 绑定事件
window.addEventListener('pushState', bindHistory)
Public dependency loading

Scenario: If both the main application and the sub-application use the same library or package (antd, axios, etc.), they can be externalsintroduced in the following way to reduce the waste of resources caused by loading duplicate packages, that is, after one project is used, the other project does not need to be loaded again. .

Way:

  • webpackMain application: configure all public dependencies externals, and index.htmluse external links to introduce these public dependencies

  • webpackSub-application: It is configured the same as the main application externals, and index.htmluses external links to introduce these public dependencies. Note that you also need to add ignoreattributes to the public dependencies of the sub-application (this is a custom attribute, a non-standard attribute), qiankun is parsing If igonrethe attribute is found, it will be automatically ignored.

Take axios as an example:

// 修改config-overrides.js
const {
    
     override, addWebpackExternals } = require('customize-cra')

module.exports = override(
  addWebpackExternals ({
    
    
    axios: "axios",
  }),
)
<!-- 注意:这里的公共依赖的版本必须一致 -->
<script ignore="true" src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>
Global state management

Generally speaking, each sub-application is divided by business. Different business lines should reduce the degree of coupling and try to avoid communication. However, if some common states or operations are involved, qiankun also supports it.

qinkun provides a global way GlobalStateto share data. After the base is initialized, sub-applications can monitor changes in this data and submit this data.

Base:

// 基座初始化
import {
    
     initGlobalState } from 'qiankun';
const actions = initGlobalState(state);
// 主项目项目监听和修改
actions.onGlobalStateChange((state, prev) => {
    
    
  // state: 变更后的状态; prev 变更前的状态
  console.log(state, prev);
});
actions.setGlobalState(state);

Sub-application:

// 子项目监听和修改
export function mount(props) {
    
    
  props.onGlobalStateChange((state, prev) => {
    
    
    // state: 变更后的状态; prev 变更前的状态
    console.log(state, prev);
  });
  props.setGlobalState(state);
}

Guess you like

Origin blog.csdn.net/weixin_43848576/article/details/130942181