This article is from a learning sharing by @onwork, a team member. I hope to share and discuss with you.

Seek to accumulate silicon step so that thousands of miles, dare to explore the beauty of life.

preface

With the stack of business volume, the project is no longer as small and beautiful as it was originally created, and gradually becomes bloated. In particular, when multiple business lines jointly maintain a huge application and reference each other, it is not easy to distinguish responsibilities, which brings inconvenience to development, debugging and release, and also increases the workload of test students.

When the back-end “micro service” is popularized, the concept of “micro” also affects the front end, resulting in the “micro front end”.

This paper briefly describes some of our experience of landing pits in practical projects.

I rolled up my sleeves, picked up the keyboard and silently typed “micro front end” into Chrome.

Iframe/single-spa/Qiankun

Scheme selection

iframe

In MDN, it is defined as an HTML inline frame element, which represents the nested browsing context. It has the ability to embed another HTML page into the current page. Each embedded browsing context has its own session history and DOM tree. The browsing context that contains embedded content is called the parent browsing context. The top-level browsing context (no parent) is usually the browser Window represented by the Window object.

Simply put, an iframe is an inline framework for embedding another document within the current HTML document. And iframe has the sandbox property, the sandbox property.

A sandbox is a virtual system application that allows you to run a browser or other application in a sandbox environment, so changes made by running it can be deleted later. It creates a sandboxed environment in which programs run without permanent impact on the hard drive. In network security, a sandbox is a tool used to test behavior such as untrusted files or applications in an isolated environment.

— Baidu Encyclopedia

“The iframe sandbox property sandbox is not supported on Internet Explorer 9 and earlier.”

advantage

  • The embedded web page can be displayed intact;
  • The sandbox mentioned above, full CSS/JS isolation;
  • Parallel loading script

disadvantage

  • Redundant CSS/JS external chain requests;
  • If multiple iframes are used in a project, there will be many x and y scroll bars.
  • Inhospitable to search engines (iframes typically only have links without innerText or innerHtml);
  • Some mobile devices cannot display iframe framework, resulting in poor compatibility.
  • Blocks the onload event on the main page

If we split up our huge application, there are 20 first-class menus, and we have to embed 20 iframes, which is a fatal problem for loading speed. So is there a better plan?

single-spa

Single-spa is a JavaScript micro-front-end framework that aggregates multiple single-page applications into a single application. Using single-SPA for front-end architecture design offers many benefits, such as:

  • Agile: independent development, more efficient deployment;
  • Smaller units: Faster testing, no need to update the entire application with each update;
  • Risk reduction: Reduce the risk of error and regression problems and shorten the troubleshooting cycle;
  • CI/CD: More efficient continuous integration, continuous deployment, and continuous delivery

See the >>> official website for examples

You can see that when we switch menus, the div in the Elements panel on the right is changing to show the contents of the corresponding menu.

Enter GET STARTED and follow the create-single-SPA process.

In the process of building, I found that I still need to explore various configuration items by myself. Is there a better and more concise scheme?

qiankun

Qiankun is a single-SPa-based microfront-end implementation library that aims to make it easier and painless to build a production-ready microfront-end architecture system.

See > > >Website example

At present, there are two mainstream micro-front-end frameworks: Single-SPA/Qiankun, which is based on single-SPA and has the following characteristics:

  • React/Vue/Angular/JQuery or any other framework.
  • HTML Entry access mode, let you access micro applications as simple as using iframe;
  • Style isolation to ensure that styles do not interfere with each other in microapplications;
  • JS sandbox to ensure that “global variables/events” do not conflict between microapplications;
  • Resource preloading: Preloading unopened micro-application resources in idle time of the browser to accelerate the opening speed of micro-application;
  • Umi plug-in provides @umiJS/plugin-Qiankun for UMI applications to switch to a micro front-end architecture system with one click

To sum up, IFrame is the simplest and direct scheme, but it is passed because of its limitations. As for the selection of Qiankun/Single-SPA, it is based on and expanded single-SPA. Backed by ant factories, it has a better ecology and a stronger community, and it is convenient to investigate problems. The entry is relatively simple, and the configuration items are much simpler than single-SPA.

Google is almost there, the rest is to start the ground, start thinking about how to do it?

First we have to look at the actual situation of the project, if our project is small, not many function points only a few pages, then it is not recommended to access the pit micro front end. If you have a large project that already has many function points, many pages, many complex interactions, or are planning a large project, you might want to try qiankun.

Project background

Pain spot demand

Generally speaking, when the project needs to use the micro front-end architecture, there will be many business modules, taking the e-commerce system as an example: login module, personal center module, order module, shopping cart module, commodity module, sales module, inventory module, logistics module, system module, report module and so on. There are also various class libraries loDash, QS, bigNum, DayJS, Echarts, STOMpJS, etc., as well as vue supporting UI, utils…

Remember how fast we created a project using VUE-CLI and then went to Yarn serve/Build. However, as our modules were connected, the project was running, the packaging was getting slower, and the deployment was getting more and more tedious, we had the same partner’s grip.

Then after running the example of Qiankun, let’s think about how to access Qiankun and what infrastructure construction work needs to be done.

Aggregate analysis

Our project was created based on VUe-CLI, vue version is 2.6.x, considering various costs, we did not expand to other front-end frameworks. There are util library, network communication AXIos, state store VUex, left menu bar, top navigation bar, internationalization, ESLint, external resources, etc. See these at the same time, we do not feel that these are very common in a VUE application, almost can not see what needs to be concerned about the place, pick up the keyboard a shuttle on the line 😏, “programming cool, BUG crematorium” 😂.

Let’s take a step down the path of micro-front-end practice (digging holes).

First Question, how can we solve the dependency problem of these common parts when we split the business? It is impossible for us to copy and paste the same tool function and do AXIos encapsulation for every project. It is unrealistic and not recommended.

Therefore, we need to divide these common parts into NPM packages, maintain them uniformly, and provide the following:

Dismantle the subcontract function
@xxx/xxx-core It is used to hold the child application initialization process and some important public methods
@xxx/xxx-ui Universal UI component library, based on element-UI re-wrapped components
@xxx/xxx-business A business component library for components that are highly coupled to the business. Strongly coupled business logic, unlike UI components
@xxx/xxx-util Utility libraries, similar to the Lodash library, are basically the util library that the rest of the libraries import
@xxx/xxx-http Axios request encapsulation provides unified management of interception, response to requests, and error handling
@xxx/xxx-config Application Configuration
@xxx/xxx-mixins Mixins related

This benefit of splitting the toolkit with NPM allows for finer granularity of control. Util/config libraries are the most basic libraries that can be referenced by other libraries to form dependencies. Well, there are now 6 libraries, and the number of libraries may expand as business needs increase, so maintaining so many NPM packages is not a big deal. How can we easily handle dependencies between libraries, version updates, and local development and debugging? That’s our next question.

Management of NPM package

Lerna is recommended for NPM package management.

Install lerna

Since lerna commands are frequently used for subsequent operations, NPX commands are not used for creation, but for global installation.

npm install -g lerna
Copy the code

Lerna initializes the project

After the installation, create a new directory and run it

lerna init --independent
Copy the code

It will be printed when it’s done

➜  xxx lerna init --independent
lerna notice cli v3.22.1
lerna info Initializing Git repository
lerna info Creating package.json
lerna info Creating lerna.json
lerna info Creating packages directory
lerna success Initialized Lerna files
Copy the code

Independent indicates the use of a separate version number, not a uniform version number (distributed to npmjs.com). We get folders packages, a lerna.json configuration file, and a package.json file.

Configuration lerna. Json

{
  "npmClient": "yarn".// replace NPM with YARN
  "useWorkspaces": true.// Enable the workspace
  "packages": [
    "packages/@xxx/*"              / / the library path]."version": "independent"."command": {
    "publish": {
      "ignoreChanges": [           // Ignore the changed file
        "**/node_modules/**"./ /...]."message": "chore: publish"}}}Copy the code

Configuration package. Json

{
  "name": "xxx".// The outermost name is optional, but we recommend semantics
  "private": true.// It must be true to enable workspaces
  "workspaces": [
    "packages/@xxx/*"             // The library path is the same as lerna.json]."scripts": {
    "cz": "cz".// Changelog commands are different from common development business logic. We need to record changes in detail
    "build-core": "lerna run build --scope @xxx/xxx-core".// Core library package command
    "build-http": "lerna run build --scope @xxx/xxx-http".// Network communication library packaging command
    "build-util": "lerna run build --scope @xxx/xxx-util".// Library package command
    "build": "concurrently \"npm run build-core\" \"npm run build-http\" \"npm run build-util\"" // Execute the command together
  },
  "keywords": [                   / / keywords
    "micro"."util"./ /...]."license": "ISC"."devDependencies": {
    "@commitlint/cli": "^ 11.0.0"."@commitlint/config-conventional": "^ 11.0.0"."commitizen": "^ holdings"."commitlint-config-cz": "^ 0.13.2"."concurrently": "^ 5.3.0." "."cz-customizable": "^ 6.3.0"."husky": "^ 4.3.8"."inquirer": "^ 7.3.3." "."lerna-changelog": "^" 1.0.1."shelljs": "^ 0.8.4"
  },
  "config": {
    "commitizen": {
      "path": "node_modules/cz-customizable"
    },
    "cz-customizable": {
      "config": ".cz-config.js"}},"husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"}},"version": "0.0.1"
}
Copy the code

NPM package directory structure

The development of NPM package

  1. Go to the xxx-util directory;
  2. runnpm init -y;
  3. Modify package.json, modify name
- "name": "@xxx/ XXX -util", + "name": "@xxx/ XXX -util", // import XXX from '@xxx/ XXX -utilCopy the code

See the article “How to Write your own library and publish it to NPM?” “To write and publish their own packages, because the main point here is that Lerna does not briefly describe” NPM package from 0 to 1 to NPM release “.

Say a small knowledge point, we refer to the content of the above link article, just to prepare, release.

So what if you run into changes that need to be debugged? We can use NPM link for debugging. Run sudo NPM link in packages/@xxx/xxx-util.

➜ xxx-util (develop) Qualify sudo NPM link /usr/local/lib/node_modules/ @xxx-util -> Those who qualify can go onto university. /Users/xxx/Desktop/micro/xxx/packages/@xxx/xxx-utilCopy the code

Import XXX from ‘@xxx/xxx-util’ in your project

➜ xxx-web-main (develop) Qualify NPM link@xxx/XXx-util /Users/xxx/Desktop/micro/xxx-web/xxx-web-main/node_modules/@xxx/xxx-util -> /usr/local/lib/node_modules/@xxx/xxx-util ->  /Users/xxx/Desktop/micro/xxx/packages/@xxx/xxx-utilCopy the code

If node_modules/@xxx/xxx-util is the same as the directory you are developing, then the link is successful, and then debugging the code is much easier.

Lerna publishes NPM packages

Lerna publish patch — Canary is used to publish alpha packages like the following:

- - util / @ XXX XXX = > 0.0.30 - alpha. 34 + d78badeCopy the code

If you want to send the official version, just leave –canary and send the other packages in the same way.

Now we can put some tools needed by our micro front end into @xxx/ XXX-util. For example, browser related, type related, validation related, value related, date related and other required tool functions can be maintained in this package as a small project.

Unified Network communication

As mentioned above, we need to break some common parts into NPM packages based on functionality aggregation for easy maintenance and reference. Here we take the common axios request encapsulation as an example.

Inside @xxx/ xxx-HTTP encapsulate, then enter

➜ XXX (develop) ✔ CD packages/@xxx/xxx-http ➜ XXX (develop) mkdir SRC // Create SRC folder for storing source code ➜ XXX (develop) mkdir lib // Create a lib folder to store your packaged files. There are a lot of packaged things. I chose "build": /node_modules/.bin/ Babel SRC --out-dir lib --extensions '.ts' --extensions '.js'" ➜ XXX (develop) CD SRC ➜ SRC (develop) mkdir config // Create a configuration folder ➜ SRC (develop) mkdir core // encapsulate core logic ➜ SRC (develop) mkdir utils // save the exported file ➜ SRC (develop) PWD/Users/XXX/Desktop/micro / / / packages / @ XXX XXX XXX - HTTP/SRC ➜ config (develop) CD config / / into the configuration folder ➜ config ➜ config (develop) touch settings.js // Create the configurationCopy the code

In httpcode. ts we store the default configuration and some types:

export enum HttpCode {
  "e400" = 400."e401" = 401."e403" = 403."e405" = 405."e408" = 408."e500" = 500."e501" = 501."e502" = 502."e503" = 503."e504" = 504."e505" = 505,}export class StatusCode {
  static 400 = 'Request not valid';
  static 401 = 'Login has timed out due to long time no operation, please log in again';
  static 403 = 'Access denied';
  static 405 = 'Unauthorized';
  static 408 = 'Request timed out';
  static 500 = 'Server internal error';
  static 501 = 'Service not implemented';
  static 502 = 'Gateway error';
  static 503 = 'Service unavailable';
  static 504 = 'Gateway timed out';
  static 505 = 'HTTP version not supported ';
}
Copy the code

Put some configuration in settings.js:

export const _httpOptions = {
  baseURL: ' './ / API base_url
  retry: 3.retryDelay: 1000.withCredentials: true.headers: {
    "Content-Type": "application/json; charset=UTF-8"
  },
  timeout: 5000.// request timeout
  method: 'post' // The default request method
}

export const _httpType = {
  DELETE: 'delete'.GET: 'get'.POST: 'post'.PUT: 'put'.PATCH: 'patch'
}
Copy the code

Enter the utils directory:

➜ config (develop) stocking CD.. && CD utils ➜ utils (develop) ➤ utils ➜ utils (develop) ➤ axios.js ➜ utils (develop) ➤ ➤ http.jsCopy the code

In axios.js, write:

import axios from "axios";
import { isObject, isArray } from "@xxx/xxx-util"
import { _httpOptions } from ".. /config/settings"; // Import configuration items
import { HttpCode, StatusCode } from ".. /config/HttpCode";
import NProgress from 'nprogress';

// Configure request interceptor
const _configRequestInterceptor = (instance, reqInterceptSuccess) = > {
  instance.interceptors.request.use(config= > {
    if (reqInterceptSuccess) {
      const _config = reqInterceptSuccess(config);
      if(! isObject(_config)) {throw Error('reqInterceptSuccess must return a Config object.')}return _config;
    }
    return config;
  }, error= > {
    return Promise.reject(error); })}/ * * *@method Configure the response interceptor *@param {Object} Instance axios instance *@param {Function} RespInterceptSuccess responds to interceptor success callback *@param {Function} RespInterceptError Responds to interceptor failure callback *@param {Number} Retry Retry times of failed requests The default value is 2 x@param {Number} RetryDelay automatic retry interval for failed requests the default value is 1000ms */
const _configResponseInterceptor = (instance, respInterceptSuccess, respInterceptError, retry, retryDelay) = > {
  // Automatic retry mechanism
  instance.defaults.retry = retry;
  instance.defaults.retryDelay = retryDelay;
  // Response interceptor
  instance.interceptors.response.use(
    res= > {
      if (respInterceptSuccess) {
        const _res = respInterceptSuccess(res);
        return _res;
      }
      return res;
    },
    err= > {
      NProgress.done();
      let config = err.config;
      let errres = err.response;
      leterr_type = errres? .status ??0;
      // Process the status code
      err.message = ErrorCodeHandler(err_type);
      // Collect error information
      if(! err.message) {switch (err_type) {
          case 404:
            err.message = 'Error requesting address:${errres? .config? .url ??'/'}`;
            break;
          default:
            err.message = "Unknown exception, please try again";
            break; }}if(! config || ! config.retry)return Promise.reject(err);
      config.__retryCount = config.__retryCount || 0;
      if (config.__retryCount >= config.retry) {
        // Custom callback that fails after repeated requests
        if (respInterceptError) {
          const _res = respInterceptError(err);
          if(! isObject(err? .config)) {throw Error('respInterceptError')}return Promise.reject(_res);
        }
        return Promise.reject(err);
      }
      config.__retryCount += 1;
      let backoff = new Promise((resolve) = > {
        setTimeout(() = > {
          resolve();
        }, config.retryDelay || 1);
      });
      return backoff.then(() = > {
        if (config.baseURL) {
          config.url = config.url.replace(config.baseURL, "");
        }
        returninstance(config); }); }); }/ * * *@param {Number} Error Indicates the error code */
const ErrorCodeHandler = (error) = > {
  return StatusCode[HttpCode[`e${error}`]]}export default class Axios {
  constructor() {
    this.httpInstance = null;
  }

  / * * *@method Create an axios instance *@param {Object} Param Configuration item *@description Retry :Number Number of retry times the default value is 2 x@description RetryDelay :Number indicates the interval at which requests fail to be automatically reconnected. The default value is 1000ms *@description Timeout :Number Specifies the timeout period for a request. The default value is 5000 *@description BaseURL :String requests the address prefix default '' *@description Expand :Object Other configuration items to be expanded Other *@param {Function} ReqInterceptSuccess requests a successful callback from the interceptor and must return a Config object *@param {Function} RespInterceptSuccess Must return a Response object * in response to the interceptor's successful callback@param {Function} RespInterceptError Responds to interceptor failure callbacks by returning a Response object *@returns Returns the created axios instance */
  static create({ retry = _httpOptions.retry, retryDelay = _httpOptions.retryDelay, withCredentials = _httpOptions.withCredentials, headers = _httpOptions.headers, timeout = _httpOptions.timeout, baseURL = _httpOptions.baseURL, ... expand } = {}, reqInterceptSuccess, respInterceptSuccess, respInterceptError) {
    // Collate configuration items
    const_options = { baseURL, withCredentials, headers, timeout, ... expand }// Create an axios instance
    const _http = axios.create(_options);
    // Register request interceptor
    _configRequestInterceptor(_http, reqInterceptSuccess);
    // Register the response interceptor
    _configResponseInterceptor(_http, respInterceptSuccess, respInterceptError, retry, retryDelay);
    this.httpInstance = _http;
    return _http;
  }

  /** * Create a single request * by passing the relevant configuration to AXIos@param {Object} param
   * @description Url :String Request address *@description Method :String Request method type Default post *@description Params :Object The URL parameter * that is to be sent with the request@description Data :Object Data that is sent as the body of the request *@description Instance :Object An instance of axios that is passed in externally. By default, it is created internally@description Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */
  static axios({ url, method = _httpOptions.method, params, data, instance, ... expand } = {}) {
    // Discard returns a new promise, note that this promise will combine HTTP errors with create AXIos
    // Collate request parameters
    const_options = { url, method, params, data, ... expand }// Process the request and return _http() directly
    const _http = instance ? instance() : this.httpInstance;
    return _http(_options);
  }

  /** * execute multiple requests *@param {Array} List axios Promise object */
  static all(list) {
    if(! isArray(list)) {throw Error('Must pass in an array! ');
    }
    return this.httpInstance.all(list)
  }
}
Copy the code

In http.js, write:

import Axios from './axios' // Import the Axios class
import { _httpType } from ".. /config/settings" // Import configuration items

export default class Http {
  / * * *@param {Object} Do not use this parameter for axios external instances without special circumstances; If passed, a custom AXIOS instance is used, and subsequent arguments will have no effect *@param {Object} axiosOptions Axios.create
   * @description Retry :Number Number of retry times the default value is 2 x@description RetryDelay :Number indicates the interval at which requests fail to be automatically reconnected. The default value is 1000ms *@description WithCredentials :Boolean Enables cross-domain requests. The default is true *@description Headers :Object Default "content-type ": "application/json; charset=UTF-8" *@description Timeout :Number Specifies the timeout period for a request. The default value is 5000 *@description BaseURL :String requests the address prefix default '' *@description Expand :Object Other configuration items that need to be expanded *@param {Function} ReqInterceptSuccess requests a successful callback from the interceptor *@param {Function} RespInterceptSuccess responds to interceptor success callback *@param {Function} RespInterceptError Responds to an interceptor failure callback */
  constructor({ axios, axiosOptions, reqInterceptSuccess, respInterceptSuccess, respInterceptError } = {}) {
    this.__http__ = axios || Axios.create(axiosOptions, reqInterceptSuccess, respInterceptSuccess, respInterceptError);
  }

  /** * get request *@param Url :String Request address *@param Params :Object The URL parameter * that is to be sent with the request@param Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */
  get(url, params, expand) {
    returnAxios.axios({ url, params, ... expand,method: _httpType.GET });
  }

  /** * the post method requests *@param Url :String Request address *@param Data :Object Data that is sent as the body of the request *@param Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */
  post(url, data, expand) {
    returnAxios.axios({ url, data, ... expand,method: _httpType.POST })
  }

  /** * the post method requests the url@param Url :String Request address *@param Params :Object The URL parameter * that is to be sent with the request@param Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */
  postQuery(url, params, expand) {
    returnAxios.axios({ url, params, ... expand,method: _httpType.POST })
  }

  /** * execute multiple concurrent requests *@param {Array} List axios Promise object */
  all(list) {
    return Axios.all(list)
  }

  /** * the delete method requests *@param Url :String Request address *@param Params :Object The URL parameter * that is to be sent with the request@param Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */
  delete(url, params, expand) {
    returnAxios.axios({ url, params, ... expand,method: _httpType.DELETE })
  }

  /** * delete method request, pass in url form *@param Url :String Request address *@param Data :Object Data that is sent as the body of the request *@param Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */
  deletePayload(url, data, expand) {
    returnAxios.axios({ url, data, ... expand,method: _httpType.DELETE })
  }

  /** * the put method requests *@param Url :String Request address *@param Data :Object Data that is sent as the body of the request *@param Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */
  put(url, data, expand) {
    returnAxios.axios({ url, data, ... expand,method: _httpType.PUT })
  }

  /** * put method request, pass the url *@param Url :String Request address *@param Params :Object The URL parameter * that is to be sent with the request@param Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */
  putQuery(url, params, expand) {
    returnAxios.axios({ url, params, ... expand,method: _httpType.PUT })
  }

  /** * patch method request *@param {Object} options
   * @description Url :String Request address *@description Data :Object Data that is sent as the body of the request *@description Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */
  patch(url, data, expand) {
    returnAxios.axios({ url, data, ... expand,method: _httpType.PATCH })
  }

   /** * Generic request method *@param {Object} options
   * @description Url :String Request address *@description Params :Object The URL parameter * that is to be sent with the request@description Data :Object Data that is sent as the body of the request *@description Instance :Object An instance of axios that is passed in externally. By default, it is created internally@description Expand :Object Extends the Object. Other unusual AXIos (options) configuration items are passed in the expand field with the same key value as the AXIos document */

  request(options) {
    return Axios.axios(options)
  }
}
Copy the code

To enter the core:

➜ config (develop) stocking CD.. && CD core ➜ core (develop) stocking ➜ core (develop) stocking touch service.jsCopy the code

In service.js, write:

import axios from 'axios';
import Http from '.. /utils/http';
import { LocalStorage, logout } from '@xxx/xxx-util';
import { Message, } from 'element-ui';
import NProgress from 'nprogress';

// Used to store the status of pending requests
const pendingRequest = [];
VUE_APP_BASE_API_GW Refer to the schema and environment variables section in the vuecli documentation
const { VUE_APP_BASE_API_GW, } = process.env;

export const NEED_MANUALLY_HANDLE_CODE_OBJ = { / / white list
  SHOW_LOG_LINK: 'B18027'.LOGISTICS_WAREHOUSE: 'B05017'.LOGISTICS_PICKUPING: 'A05093'.LOGISTICS_SOME_PICKUPING: 'A05028'};/ / NProgress configuration
NProgress.configure({ showSpinner: false});/ / configuration items
const options = {
  axiosOptions: { baseURL: VUE_APP_BASE_API_GW, },
  reqInterceptSuccess: config= > {
    // Open the Progress bar
    NProgress.start();
    const token = LocalStorage.getToken();
    // Add language parameters to the header
    config.headers['x-ca-language'] = LocalStorage.getLanguage() === 'en' ? 'en_US' : 'zh_CN';
    // Add the request's unique ID
    config.headers['x-ca-reqid'] = Math.random() + new Date().getTime();
    // Add the request timestamp
    config.headers['x-ca-reqtime'] = new Date().getTime();
    if (token) {
      // Allow each request to carry token--['Authorization'] is a custom key. Modify it based on actual conditions
      config.headers['Authorization'] = 'bearer ' + token;
    }
    BaseURL}${config. method} ${config.baseURL}${config.url}
    if(config.cancelToken ! = =false) {
      const requestMark = `${config.method}-${config.url}`;
      PendingRequest = pendingRequest = pendingRequest = pendingRequest
      const markIndex = pendingRequest.findIndex(item= > {
          return item.name === requestMark;
      });
      // Exist, that is, repeat
      if (markIndex > -1) {
          // Cancel the previous duplicate request
          pendingRequest[markIndex].cancel();
          // Delete the request identifier in pendingRequest
          pendingRequest.splice(markIndex, 1);
      }
      // (re) create a cancelToken for axios for this request
      const CancelToken = axios.CancelToken;
      const source = CancelToken.source();
      config.cancelToken = source.token;
      // Set up a custom configuration requestMark item, which is mainly used in response interception
      config.requestMark = requestMark;
      // Record the identity of this request
      pendingRequest.push({
          name: requestMark,
          cancel: source.cancel,
      });
    }
    return config;
  },
  respInterceptSuccess: res= > {
    // Close the Progress bar
    NProgress.done();
    if(res.config.cancelToken ! = =false) {// Find the pendingRequest identifier based on the requestMark configuration set in request interception
      const markIndex = pendingRequest.findIndex(item= > {
        return item.name === res.config.requestMark;
      });
      // Delete the flag
      markIndex > -1 && pendingRequest.splice(markIndex, 1);
    }
    const { status, data, } = res;
    const { code, data: dataForm, msg, error_description: errorDescription, } = data
    // If 401, jump to the login page
    if (status === 401) {
      logout();
      location.reload();
    }
    // If the request is not 200, no will be processed by default
    if(status ! = =200) {
      const message = msg || errorDescription || 'Unknown error';
      return Promise.reject(new Error(message));
    }
    // B01032 Login timed out
    if (code === 'B01032' || code === 'A00998') {
      logout();
      Message.error({
        message: 'Login timed out, please log in again'.duration: 250.onClose: () = >{ location.reload(); }});return Promise.reject(new Error('Login timed out, please log in again'));
    }

    if(code ! = ='000000') {
        const message = msg || errorDescription || 'Unknown error';
        // The logic that needs to be handled manually on the page
        if (Object.values(NEED_MANUALLY_HANDLE_CODE_OBJ).includes(code)) {
            return Promise.reject({
                message,
                code,
            });
        }
        Message.error(message);
        return Promise.reject(new Error(message));
    }
    return dataForm;
  },
  respInterceptError: error= > {
    NProgress.done();
    if (axios.isCancel(error)) {
        // Manually cancelled requests close the Promise chain
        return new Promise(() = > ({}));
    }
    return Promise.reject(new Error(error)); }};// instantiate HTTP
const http = new Http(options);

export default http;
Copy the code

The service exports an instance of Axios, and we need a file to hold it for packaging

➜ core (develop) ✔ PWD/Users/XXX/Desktop/micro / / / packages / @ XXX XXX XXX - HTTP/SRC/core ➜ core (develop) ✔ CD.. && touch index.jsCopy the code

Export this instance in index.js for other projects to reference

import http from "./core/service"
export default http;
Copy the code

Similarly, we use Lerna to do the release.

So far, we’ve written the network request library. Yarn add – d@xxx /xxx-core in Example: yarn add – d@xxx /xxx-core in example: yarn add – d@xxx /xxx-core

The simple example provided by Qiankun’s official website obviously cannot be used directly. We need to make many expansions, from login to the left menu bar, to the top navigation bar and to the right content rendering. In the next article, we will look at how to create a master/child app, how to integrate Keep-Alive and VUex, and how to unify versions of common dependencies.

Due to confidentiality agreement, @xxx refers to the package with domain, similar to @vue, XXX is the same English.

That is all the content of this sharing. I hope it will help you

Like the words don’t forget to move your fingers, like, collect, pay attention to three even a wave away.


About us

We are the front end team, left hand component library, right hand tool library, all kinds of technology savage growth.

A man can’t run as fast as a crowd can run. Welcome to join our team, the year of the Ox is booming forward.