Zhang Yanqing

Business background

Cloud music advertising DSP (demander platform) platform is divided into contract platform (VUE framework) and bidding platform (REACT framework). Due to historical reasons, the selection of framework has not been unified. Recently, new requirements have come up, and the same modules need to be added to the two platforms at the same time, because both platforms are DSP platforms. Therefore, considering the reuse of components and the reduction of maintenance costs, I am thinking of how to unify the technology stack and put the React system into the Vue project for presentation.

The system is a traditional left and right layout, the left sidebar shows the menu bar, the header navigation shows the basic information, and the application content is all filled into the blue content area.


To be honest, my first thought was to nest iframes, but if you’ve ever worked with iframes, you know how painful it is:

  • Application of communication<div style=”margin-top:5px” >

    Sometimes the main application only wants to know the URL parameters of the subsystem, but the iframe application has a different source, so you have to find other ways to get the parameters. The one we use most often is thispostMessage

    <div style=”margin-top:5px”>
  • The cache<div style=”margin-top:5px” />

    iframeAfter the application update goes online, the system will find that the system hit the cache to display the old content, which needs to be solved with a timestamp scheme or forced to refresh

In addition, MPA + routing is used for distribution. When users visit pages, NGINX and others are responsible for distribution to different business applications according to routing, and each business application returns to the browser after completing the assembly of resources. In this way, the interface and navigation are made into a similar look.

  • Advantages:

    • Multi-framework development;
    • Independent deployment and operation;
    • Complete isolation between applications.
  • Disadvantages:

    • Poor experience, long load times per individual app;
    • Due to complete isolation, common areas such as navigation and top are changed a lot, and the reusability is very poor.

<div style=”margin-top:5px” /> <div style=”margin-top:5px” />

  • Base pattern: mainly based on routing distribution, a base application listens for routing and loads different applications according to routing rules to achieve decoupling between applications
  • EMP: Webpack5 Module Federation, a decentralized micro-front-end solution, can easily realize resource sharing and communication between applications on the basis of application isolation;

In general, iframes are used primarily for simple third-party systems with low performance requirements; MPA can not meet the current business needs in terms of implementation cost and experience; Both the base mode and EMP were good choices, as Qiankun was widely used and mature in the industry, so I finally chose Qiankun

(Qiankun)

Qiankun is a front-end micro-service framework based on Single-SPA implemented by Ant Financial. In essence, it is a routed and distributed service framework, which is different from the original Single-SPA solution that uses JS Entry to load sub-applications. Qiankun uses HTML Entry as an alternative optimization. Use restrictions for JS Entry:

  • Restrict a JS entry file
  • Static resources such as images and CSS need to be packaged into JS
  • Code Splitting cannot be applied

< br / > contrast JS Entry, too many HTML Entry use is convenient, the project configuration given Entry documents, qiankun will Fetch request on its own resources, after parsing the JS and CSS file resources, inserted into a given container, perfect ~

The way JS Entry is usually done is when the child application types the resource into an Entry Script, similar to Single-Spa
example; <div style=”margin-top:5px” />


HTML Entry is an organization of child application resources in HTML format. The main application obtains static resources of the child application through FETCH HTML, and at the same time, it stuffs HTML Document into the container of the main application as the child node. It is more readable and maintainable, closer to what the final page looks like after it has been mounted, and there are no issues that require two-way escapes.

Project practice

Since the VUE project has been developed, we need to carry out transformation in the original project. Obviously, the VUE project is selected as the base application, and the React child application is built by the Create React App for the development of new requirements. Next, let’s take a look at the specific implementation

The application of the base is modified

The base (main) is built by Vue-CLI, and we keep the original code structure and logic unchanged. On this basis, we provide a separate mount container DIV for the children, which is also filled in the same content display area. Qiankun only needs to be introduced into the base application. In order to facilitate management, we added a new directory named micro. In the identification directory is the micro-front-end modification code, and initialized the global configuration

// route configuration const apps = [{name: 'reactMicroApp ', entry: '//localhost:10100', container: '#frame', activeRule: '/react' } ];

Apply the configuration registration function

import { registerMicroApps, start } from "qiankun"; import apps from "./apps"; Export const registerApp = () => registerMicroApps(apps); export const registerApp = () => registerMicroApps(apps); // Export Default Start from Qiankun;

Layout components

<section class="app-main"> <transition v-show="$route.name" name="fade-transform" mode="out-in"> <! > <router-view /> </transition> <! -- child application rendering area for mounting child application nodes --> <div id="frame" /> </section>
import startQiankun, { registerApp } from ".. /.. /.. /micro"; Export default {name: "AppMain", mounted() {// Mounted (); startQiankun(); }};

Two important APIs from Qiankun are used here:

  • registerMicroApps
  • start

Note: We chose to initiate the configuration during the mounted life cycle to ensure that the mount container does exist


Let’s take a look at the process of the Qiankun Registrar application in detail:


  • After dependency injection, the identification variable parameters are initializedxx_QIANKUN__, which is used to judge the environment of the child application
  • Qiankun uses ActiveRule rules to determine whether to activate a child application

    • activeRuleIs a string, it is intercepted autonomously as a route interceptor
    • activeRuleIs a function, according to the return value of the function to determine whether it is active

<! —->

  • When the child application is activated, the static resource address of the child application is resolved through HTML-Entry and mounted on the corresponding container
  • Create a sandbox environment, find child application lifecycle functions, and initialize child applications

Build Qiankun child applications

We created a React project application based on the Create React App. As described by the above process, we knew that the child application had to expose a series of life cycle functions for Qiankun to call. We made the transformation in index.js file: Js file. When the child application is mounted under the main application, if some of our static resources follow the publicPath=/ configuration, the domain name we get will be the main application domain name. This will cause a resource load error. Fortunately, Webpack provides the following method for fixing it:

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

Routing Base setting because typically, the main application will intercept browser routing changes to activate the loader child application. For example, in the code above our route configuration and activation rule is written activeRule: /react. What does this mean? This means that the child application is activated when the browser pathname matches /react, but if our child application routing configuration is something like this:

<Router> 
 <Route exact path="/" component={Home} />
 <Route path="/list" component={List} /> 
</Router> 

How do we make the domain name /react load the corresponding component correctly? You must have experienced the need to use domain name secondary directory access, here is the same, we judge whether in Qiankun environment, adjust the base, as follows:

const BASE_NAME = window.__POWERED_BY_QIANKUN__ ? "/react" : ""; . <Router base={BASE_NAME}> ... </Router>

Add the entry file of life cycle function child application to add life cycle function initialization to facilitate the main application to call the life cycle of child application by the name of the application after the completion of calling resources

/** * Bootstrap is called only once when the micro-application is initialized. The next time the micro-application reenters, it calls the mount hook directly. Bootstrap will not be triggered again. This is usually where we can do initialization of global variables, such as application-level caches that are not destroyed during the unmount phase. */ export async function bootstrap() { console.log("bootstraped"); } /** * export async function mount(props) {console.log("mount", props); render(props); / / export async function unmount() {console.log("unmount"); / / export async function unmount() {console.log("unmount"); ReactDOM.unmountComponentAtNode(document.getElementById("root")); }

Note: All generative periodic functions must be Promise


Modify the packaging configuration

Module. exports = {webpack: (config) => {// exports the package name of the microapp, which is the same as the registered microapp name in the main app config.output.library = 'reactMicroApp'; / / expose your library for all of the module definition can be run under the way of config. The output. LibraryTarget = "umd"; / / on-demand load, can be set to webpackJsonp_ReactMicroApp config. The output. The jsonpFunction = ` webpackJsonp_ReactMicroApp `; config.resolve.alias = { ... config.resolve.alias, "@": path.resolve(__dirname, "src"), }; return config; }, devServer: function (configFunction) { return function (proxy, allowedHost) { const config = configFunction(proxy, allowedHost); // Turn off the host check so that the microapplication can be fetched config.disableHostCheck = true; // config.headers = {" Access-Control-Allege-Origin ": "*",}; / / configure the history mode config. HistoryApiFallback = true; return config; }; }};

Note: The configuration changes serve two purposes, one is to expose the lifecycle functions to the host application and the second is to allow cross-domain access. Note points for changes can be referenced in the code comments.

  • Expose Lifecycle: UMD can have Qiankun match to a lifecycle function by application name
  • Cross-domain configuration: The main application fetches resources through the FETCH, so in order to solve the cross-domain problem, it must be set to allow cross-domain access

Summary: The jump process is summarized. In the main application Router, the child application jump path is defined, as shown in the figure below. In the Mounted Life Cycle of the calling component, the exposure of Qiankun is used
loadMicroAppMethod loads the child application, jumps to the route defined by the child application, and uses the
addGlobalUncaughtErrorHandler
removeGlobalUncaughtErrorHandlerListen for and handle exceptional conditions (such as child application loading failure). When the child application listens for the jump route, load the child application (above)
<Router>Component that completes the jump from the main application to the child application.

{
 path: '/xxx',
 component: Layout,
 children: [
 {
 path: '/xxx',
 component: () => import('@/micro/app/react'),
 meta: { title: 'xxx', icon: 'user' }
 }
 ]
 },

Problems encountered in the project

If we find that the child application system has not been loaded after the project is started, we should open the console to analyze the reason:

  • Console no error: child application is not active, check the activation rule configuration is correct
  • No mount container found: Check if the container DIV must be present when Qiankun start. If not, try to execute it after DOM mount.


























The reason is simple. Assuming the child application is in history mode, it is difficult to activate the rule to match the child application by changing the pathname every time the route is switched. Since Qiankun does not turn on the CSS sandbox for style isolation by default, we can enable {StrictStyleIsolation: This will wrap the child application with a Shadow DOM node. This will be familiar to you. It will be the same as the style isolation scheme used in WeChat applets for pages and custom components. 4. In addition, several points needing attention are summarized in the access process

  • Although Qiankun supports jQuery, it is not very friendly to access the old project of multi-page application, which requires modification of every page, and the cost is high. Iframe is preferred for such old project access.
  • Because Qiankun extracts JS files and DOM structures through HTML-Entry, it actually shares the same Document with the main application. If the child application and the main application define the same event at the same time, they will affect each other, such as using theonClickaddEventListener<body>Add a click event, JS sandbox does not eliminate its impact, but also rely on the usual code specifications
  • Deployment is a bit cumbersome and cross-domain issues need to be resolved manually

5. There may be some issues to consider in the future

  • Common components rely on reuse: I may not want to write the same set of request wrapping code in a child application that is unavoidable in a project such as request library wrapping
  • Automated injection: The process of transforming each child application is actually a troublesome thing, but in fact most of the work is standardized process. We are considering automatic registration of child applications through scripts to achieve automation

conclusion

In fact, writing down the whole project, the biggest feeling of Qiankun out of the box availability is very strong, the need to change the basic configuration of the project is very few, of course, encountered some pits must be stepped on to be more clear. If there are any problems or mistakes in the article, please correct me. Thank you!

This article is posted from
Netease cloud music big front end teamThis article is not allowed to be reproduced in any form without authorization. We recruit front-end, iOS, and Android all year round. If you’re ready for a career change and you happen to like cloud music, then join us. Grp.music-fe (at) corp.netease.com!