Business background

First look at the results, the complete project online demo address point I check

Dapeng Map visualization large screen project is a large screen application integrating map application and view application. View application can be switched through the head menu. View application includes content display areas on the left and right sides, and view application can communicate and interact with map application. The project was built by Vue3. The core problem was that if 3D maps were integrated in iframe way, the performance and user experience would be greatly reduced. To solve this problem, we used the micro-front-end service framework Qiankun to successfully embed map H5 application and Vue3 view application into the same page in the way of DOM. These embedded applications are called microapplications, and the map and view applications in the figure below are both microapplications.

Why the micro front end solution

  • 1. Technology stack independent – Support access to any technology stack applications, support any future technology stack

    • Imagine that angular. js was a hot technology stack five years ago, and many large projects were using it. However, the technology was updated every year and the front-end only learned to use the latest technology stack every year. Now it’s at a point where nobody wants to change, nobody wants to optimize, nobody wants to transplant, nobody even wants to change.

  • 2. Independent development, testing, deployment – different teams or personnel to maintain the corresponding application, responsibility separation, decoupled from the boulder, accelerate construction and development

    • Can you imagine putting Baidu and Google on the same page at the same time? Even qq music, netease cloud music, cool dog music in a page to run? That’s right, the micro front end allows you to take an application and deploy it on its own at any time, and integrate it into a new application on a single base.
  • 3. Incremental upgrades – Faster and more targeted upgrades without packing the entire code

    • Instead of the traditional package upgrade method of the whole application, the micro front-end solution is to combine the application with smaller granularity into a large application, so that only the small application can be upgraded, just like if you have a house, only one room needs to be upgraded, and the rest of the room is left untouched.

  • 4. Independent runtime

    • Each micro-application has its own independent runtime context, which means that their JS and CSS environments are not affected by each other, for exampleAThe application has changedwindow.a.BThe app has also been modifiedwindow.aBut theirwindowNot the samewindowObject, so it will not cause variable pollution.

Project practice

Base application modification

Vue-cli is used to build the base, and vuE-CLI is also used to build the sub-applications. The technology stack is Vue3. The subsequent access sub-applications can access the applications of other technology stacks.

Install qiankun

npm install qiankun -P
Copy the code

The routing configuration

Make /home the common main route for the entire base and child applications

// router/index.ts
const Login = () = > import('.. /views/login.vue');
const Home = () = > import('.. /views/home.vue');
const NotFound = () = > import('@/components/exception/not-found.vue');

// The route that must be loaded the first time
const routes: Array<any> = [
  // The home page is displayed by default
  {
    path: ' '.redirect: {
      name: 'home',}}, {path: '/'.redirect: {
      name: 'home',}}, {path: '/home'.name: 'home'.component: Home,
  },
  // Return 404 in case of no match at all
  {
    path: '/:catchAll(.*)'.component: NotFound,
  },
];

export default routes;
Copy the code

The base creates hash routes using createWebHashHistory, or history and memory routes

// main.ts
import routes from './router';

function render() {
  router = createRouter({
    history: createWebHashHistory(),
    routes,
  });
  
  instance.use(router);
}
Copy the code

Configuration navigation menu

Click the menu to switch the view subapplication, so each option in the menu should contain an application information, including the directory ID, name, entry, mount container ID, to switch the view microapplication.

The data structure of the navigation directory is as follows. In real scenarios, the data is obtained through interfaces for dynamic change

// menu.ts
export default [{
	"id": 1.Id / / directory
	"name": "Blue sky".// Directory name
	"app": {
		"name": "vue3-air-app".// Application name
		"entry": {
			"dev": "//localhost:8081/".// Developer app entry
			"product": "http://182.48.115.108:8887/vue3-air-app/" // Online version app entry
		},
		"container": "#microapp" // Mount container ID
	},
	"active": false
}, {
	"id": 2."name": "Clear water"."app": {
		"name": "vue3-water-app"."entry": {
			"dev": "//localhost:8082/"."product": "http://182.48.115.108:8887/vue3-water-app/"
		},
		"container": "#microapp"
	},
	"active": false
},
……
]
Copy the code

When the menu is switched, the view application can be switched by loading the application information in the menu by loadMicroApp, for example

 import { loadMicroApp } from 'qiankun';
 import MENU from './menu';
 const isProd = process.env.NODE_ENV === 'production'; // Whether to develop the environment
 
 // Menu switch
 function onChangeMenu(id) {
   const currentApp = MENU.find(menu= > menu.id === id).app;
   loadMicroApp({
      name: currentApp.name,
      entry: currentApp.entry[isProd ? 'dev' : 'product'].container: currentApp.container,
      props: {}}); }Copy the code

Loading microapplications

There are two ways for the base to load the microapplication. One is to register the sub-application information through registerMicroApps, including the name, entry, mount container ID and route matching rule of the sub-application. After registration application will be based on the change of browser url to match the corresponding applications and loads, the second is to manually by loadMicroApp load the application, the name of the son is also a need to application, entrance, mount the container id, but is less routing matching rules, he can make your child application mount immediately, do not need to match any routing rules, This project uses loadMicroApp, because map application and view application need to be loaded at the same time.

  • Instructions for using loadMicroApp
    • Action: throughqiankunloadMicroAppThe mount/unmount function implements child applications in the base.
    • Advantages: Multiple microapplications can be mounted simultaneously on a single page, such as a map application and a view application.
    • Disadvantages: Applications cannot be mounted based on route matching rules, because a route can match only one application.
    • Application scenario: When more than two sub-applications need to be mounted on a page and do not need route matching to mount the sub-applications.
  • The demo presentation

  • Code implementation
<! -- template -->
<template>
    <div class="container">
        <div id="micro-app1"></div>
        <div id="micro-app2"></div>
    </div>
</template>
Copy the code
 // main.ts
 import { loadMicroApp } from 'qiankun';
 loadMicroApp({
    name: 'app1'.// Unique name of the application
    entry: '//localhost:8088/'.// Apply a unique HTML entry. You can omit index.html
    container: '#micro-app1'.// The DOM id of the container to which the base mounts the application
    props: {} // The parameter that the base passes to the microapplication, which is obtained from the parameter props of the mount lifecycle function
 });
 
 loadMicroApp({
    name: 'app2'.entry: '//localhost:8089/'.container: '#micro-app2'.props: {}});Copy the code

Note: It is an error for loadMicroApp to repeatedly mount an application with the same name as container, as shown below

 // main.ts
 import { loadMicroApp } from 'qiankun';
 loadMicroApp({
    name: 'app1'.// Unique name of the application
    entry: '//localhost:8088/'.// Apply a unique HTML entry. You can omit index.html
    container: '#micro-app1'.// The DOM id of the container to which the base mounts the application
    props: {} // The parameter that the base passes to the microapplication, which is obtained from the parameter props of the mount lifecycle function
 });
 
 loadMicroApp({
    name: 'app2'.entry: '//localhost:8088/'.container: '#micro-app2'.props: {}});// The page will go blank after the above application is repeatedly mounted
 loadMicroApp({
    name: 'app2'.entry: '//localhost:8089/'.container: '#micro-app2'.props: {}});Copy the code

Solution: LoadMicroApp further encapsulates the switchMicroApp function to determine how to switch applications according to the mounting condition of the applications. LoadMicroApp is directly called to load the applications when the applications are mounted for the first time. You need to unmount the previously mounted application before mounting the new application.

  import { LoadableApp, loadMicroApp } from 'qiankun';
  
  // loadMicroApp instance object
  const contentApp: any = ref(null);

  /** * Switch microapplications *@param RunningMicroApp Qiankun regulated microapplication configuration object */
  function switchMicroApp(runningMicroApp: LoadableApp<any>) {
    const microApp = runningMicroApp;

    // Uninstall the previous microapplication before switching the microapplication
    if(microApp && contentApp? .value? .getStatus() ==='MOUNTED') {
      // Uninstall the previous application
      contentApp.value.unmount();
      // After uninstalling the previous application, the new application will be loaded. In this case, the qiankun loadMicroApp will be used to load the microapplication.
      contentApp.value = loadMicroApp(microApp);

      return;
    }

    // If the microapplication is being loaded for the first time, it can be loaded directly without first unmounting the previously mounted application
    contentApp.value = loadMicroApp(microApp);
  }
  
 switchMicroApp({
    name: 'app2'.entry: '//localhost:8088/'.container: '#micro-app2'.props: {}});// Mount the above application again
 switchMicroApp({
    name: 'app2'.entry: '//localhost:8089/'.container: '#micro-app2'.props: {}});Copy the code

Layout components

<template>
  <div class="ths-main">
    <! -- Navigation menu -->
    <t-header :data="headerNavs" @click="onClickHeader"></t-header>
    
    <! -- View microapplication Container -->
    <div class="content" id="microapp"></div>

    <div class="map">
      <div class="map-mask"></div>
      <! -- Map microapplication container -->
      <div id="map-app"></div>
    </div>
  </div></template> <script lang="ts"> export default defineComponent({ name: 'Home', setup() {const headerNavs = ref<HeaderNavItems>([]); Const curAppId = ref<number>(0); Const BASE_MICRO_APPS: BaseMicroApps = {map: {id: 99, name: 'h5-map-app', entry:! isProd ? '//localhost:8088/' : `${webUrl}h5-map-app/`, container: '#map-app', props: { baseUrl, experimentalStyleIsolation: true, }, }, home: { id: 0, name: 'vue3-home-app', entry: ! isProd ? '//localhost:8090/' : `${webUrl}vue3-home-app/`, container: '#microapp', props: { baseUrl, experimentalStyleIsolation: false, }, }, }; If (curappid. value === id) {return; if (curappid. value === id) {return; } // The selected app id curappid. value = id; ForEach ((item) => {if (item.id === id) {switchMicroApp(item.id, {... item.app, entry: item.app.entry[isProd ? 'product' : 'dev'], }); }}); } onBeforeMount(() => {// Default to load the map application loadMicroApp(base_micro_apps.map); // loadMicroApp(base_micro_apps.home); }); OnMounted (() => {getHeaderMenu('dapeng-header-menu'). Then ((data: HeaderNavItems) => { if (isEmpty(data)) { headerNavs.value = []; return false; } headerNavs.value = data; return true; }); }); return { onClickHeader, }; }}); </script>Copy the code

Refresh routes to save application status

Because the application loaded by loadMicroApp cannot match the route, it cannot refresh or keep the state when the route changes. Once the route is refreshed, the application loaded in the base will be reset to the application loaded for the first time, for example, the application in the atmosphere and water environment will be re-rendered as the home page application after refreshing.

  • The solution
    • Each application corresponds to an ID of the menu, so it passeslocalStorageCache the way to switch the menu ID, and then refresh the route according tolocalStorageYou can switch the id corresponding to the application.

Preload microapplications

After obtaining the menu data containing all sub-application information, static resources such as HTML, JS and CSS of other sub-applications can be requested in advance. When switching sub-applications, these static resources can be directly read from the cache to speed up the rendering of sub-applications.

import { prefetchApps } from 'qiankun';

getHeaderMenu('dapeng-header-menu').then((data: HeaderNavItems) = > {
  if (isEmpty(data)) {
    headerNavs.value = [];
    return false;
  }
  headerNavs.value = data;
  // Preload the rest of the microapplications
  prefetchApps(headerNavs.value.map(nav= > ({
    name: nav.app.name,
    entry: nav.app.entry[isProd ? 'product' : 'dev']})));return true;
});
Copy the code

Build qiankun sub-application

We created a Vue3 project application based on fe-CLI of the company. According to the above process description, we knew that the sub-application had to expose a series of life cycle functions for qiankun to call and transform in the index.js file:

Add the public-path.ts file

Add the public-path.ts file to the outer directory. When the child application is mounted under the master application, if some static resources are configured with publicPath=/, the domain name we get will be the master application domain name, which will cause resource loading errors. Fortunately, Webpack provides a way to dynamically change the publicPath of __webpack_public_path__. Window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ is equivalent to location.host + location.pathname such as http://localhost:8081/ or http://182.48.115.108:8887/vue3-air-home/, is as follows:

// public-path.ts
if ((window as any).__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
Copy the code

Route Base Settings

Imagine what happens when the base’s routing base is no longer local ‘/’, but online ‘/ microfe-dapeng-base /’, while the sub-application’s routing base is set to ‘/’. Yes, The answer is that the ‘/ microfe-dapeng-base /’ route cannot be matched, resulting in the local sub-application route cannot be matched and resources cannot be loaded. The first parameter to createWebHistory is the route base to be set.

const isProd = process.env.NODE_ENV === 'production'; // Whether to develop the environment
const BASE_PREFIX = isProd ? '/microFE-dapeng-base/' : '/'; // Set base based on the environment
router = createRouter({
  // Set base based on whether the qiankun environment is available
  history: createWebHistory((window as any).__POWERED_BY_QIANKUN__ ? BASE_PREFIX : '/'),
  routes,
});
Copy the code

The base will always be ‘/’ as long as it is in the development environment and not in the qiankun environment or running independently, because the local development base will not set the domain level 2 directory. In an online environment, if the child application is running independently, then base is ‘/’ relative to the current root path; If it is an qiankun environment, then base is ‘/ microfe-dapeng-base /’ relative to base routing.

Add life cycle functions

Life cycle function initialization is added to the entry file of the subapplication to facilitate the active application to invoke the life cycle of the subapplication based on the application name after the resource is invoked

/** * Bootstrap will only be called once during the micro-application initialization. The next time the micro-application re-enters, the mount hook will be called directly. Bootstrap will not be triggered again. * This is usually where we can initialize global variables, such as application-level caches that will not be destroyed during the unmount phase. * /
export async function bootstrap() {
  console.log("bootstraped");
}

/** * The application calls the mount method every time it enters, and usually we trigger the application's render method here */
export async function mount(props) {
  console.log("mount", props);
  render(props); // Execute createApp to create instance and mount the DOM of the child application
}

/** * The method that is called each time the application is cut/unloaded, usually here we unload the application instance of the microapplication */
export async function unmount() {
  console.log("unmount");
  instance.unmount();
  instance._container.innerHTML = ' ';
  instance = null;
  router = null;
}
Copy the code

Note: All generative periodic functions must be promises

Sub-applications run configurations independently

The lifecycle mount hook is used to mount the DOM of the instance of the child application. If the child application is to run separately, do you want to mount the DOM of the instance as well? Through! Window.__POWERED_BY_QIANKUN__ Determine if it is not the qiankun environment, immediately mount the DOM of the instance.

function render(props) {
    const container = props.container || null;
    const isProd = process.env.NODE_ENV === 'production'; // Whether to develop the environment
    const BASE_PREFIX = isProd ? '/microFE-dapeng-base/' : '/'; // Set base based on the environment
    router = createRouter({
      history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? BASE_PREFIX : '/'),
      routes,
    });
    instance = createApp(App);
    instance.use(router);
    instance.mount(container ? container.querySelector('#app') : '#app');
}

// Execute render to mount the DOM immediately when not in the Qiankun environment
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

// Lifecycle hook functions
export async function bootstrap() {... }// In the Qiankun environment, the parent container will be mounted and the DOM will be mounted with render
export async function mount(props) { render(props) }
export async function unmount() {... }Copy the code

Modifying the packaging Configuration

function isProd() {
  return process.env.NODE_ENV === 'production';
}
// Image public address prefix, please set to the full root address of the micro-application deployed on the three-party server
const publicPath = isProd() ? ` http://182.48.115.108:8887/${name}/ ` : `http://localhost:${port}`;

module.exports = {
  // Local service configuration
  devServer: {
    host: 'localhost'.hot: true.disableHostCheck: true,
    port,
    overlay: {
      warnings: false.errors: true,},headers: {
       // Set local resources to allow cross-domain. After deployment, the server also needs to set cross-domain, because the base uses fetch to pull sub-application resources, and cross-domain can only be pulled
      'Access-Control-Allow-Origin': The '*',
    },
  },
  publicPath, // It is important to note that the load path of all static resources is set to absolute
  configureWebpack: (config) = > {
      return {
          output: {
            // The package name of the micro-app, which is the same as the registered micro-app name in the main app, e.g. Name = 'vue3-home-app'
            library: `${name}-[name]`.// Expose your library in a way that all module definitions work
            libraryTarget: 'umd'.WebpackJsonp_VueMicroApp = webpackJsonp_VueMicroApp
            jsonpFunction: 'webpackJsonp_VueMicroApp',}}},chainWebpack: (config) = > {
    // Replace the packaged font resource address with absolute address or compress it into Base64
    config.module.rule('fonts')
      .use('url-loader')
      .loader('url-loader')
      .options({
        limit: 4096.< 4KB will be packaged as base64
        fallback: {
          loader: 'file-loader'.options: {
            name: 'fonts/[name].[hash:8].[ext]'.// Replace the image relative address with the full local or online address to prevent the relative address from looking for static resources relative to the base address
            / / such as' http://localhost:8082/ 'or' http://182.48.115.108:8887/vue3-air-app/ '
            publicPath,
          },
        },
      })
      .end();
    // Replace the packaged image resource address with absolute or compressed base64
    config.module.rule('images')
      .use('url-loader')
      .loader('url-loader')
      .options({
        limit: 4096.< 4KB will be packaged as base64
        fallback: {
          loader: 'file-loader'.options: {
            name: 'img/[name].[hash:8].[ext]'./ / same as abovepublicPath, }, }, }); }};Copy the code

Note: the configuration changes in order to achieve three, one is exposed to the life cycle function calls to the main application, the second is allowed to cross domain access, the third point is the relative path of static resources, including the picture address modification to the absolute path to solve the problem of resources relative to the base of the path, modify the attention points can reference code comments.

  • Exposed Life Cycle: UMD allows Qiankun to match life cycle functions by application name
  • Cross-domain configuration: The primary application fetches resources through Fetch, so to solve the cross-domain problem, it must be set to allow cross-domain access

Problems encountered in the project

1. The sub-application fails to be loaded

If the sub-application system is not loaded after the project is started, we should open the console to analyze the cause:

  • Console error free: child app not loaded, check whether render mount DOM was called in the child app export lifecycle mount
  • The mount container was not found: Checks whether the container DIV is presentloadMicroAppIf not, try to execute after DOM mount.

2. Base applies routing mode

Base routing configuration

exports routes = [{
    path: '/home'.name: 'home'.component: Home,
  }];
Copy the code
  • Base application projects are hash mode routes and child routes are History mode

Configure routes for sub-applications

// routes.ts
export default[{path: '/'.// This must be '/' and cannot change the pathname
    name: 'Home'.component: () = > import('@/views/home.vue'),},];// main.ts
router = createRouter({
    / / the history model
    history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? BASE_PREFIX : '/'),
    routes,
  });
Copy the code
  • Base application items are hash mode routes and child routes are hash mode routes

Configure routes for sub-applications

// routes.ts
export default[{path: '/home'.// This must be consistent with the base
    name: 'Home'.component: () = > import('@/views/home.vue'),},];// main.ts
router = createRouter({
    / / hash pattern
    history: createWebHashHistory(window.__POWERED_BY_QIANKUN__ ? BASE_PREFIX : '/'),
    routes,
  });
Copy the code

3, CSS style disorder

Since the CSS sandbox is not enabled for style isolation by default, there are two styles isolation configurations when the master and child applications have style errors:

  • strictStyleIsolation – boolean
    • It will be used at this timeShadow DomNodes wrap child applications, which I’m sure you’re familiar with seeing, andIonicConsistent style isolation scheme for components in.
    loadMicroApp(microApp, {
      sandbox: {
        strictStyleIsolation: true,}});Copy the code
    • advantages
      • Fully insulate CSS styles
    • disadvantages
      • When using some popover components (popovers are often added to document.body by default) it skips the shadow boundary and goes into the main application, and the style is lost
  • experimentalStyleIsolation – boolean
    • Rules for applying style are hijacked at run time and prefixes are added to control isolation, for example.titleAfter hijacking outputdiv[data-qiankun-app] .title
    loadMicroApp(microApp, {
      sandbox: {
        experimentalStyleIsolation: true,}});Copy the code
    • advantages
      • Supports most style isolation requirements
      • Resolved root node loss caused by Shadow DOM scheme
    • disadvantages
      • There is a performance penalty for reloading styles at run time

Refer to this article for details on how style isolation works

4. H5 microapplies static resource 404

If the H5 sub-application is not packaged with tools such as WebPack, and does not replace the static resource relative address with publicPath at the time of packaging, then the same problem remains. The application is converted to DOM and appended to the base HTML. The relative path has been changed from the original application URL to the current page, the base URL.

<! -- -- -- > local
<base href="http://localhost:8088/">
<! Online -- -- - >
<base href="http://182.48.115.108:8887/h5-map-app/">
Copy the code

5. The global scope of the application loaded by loadMicroApp will be out of order if other JS are loaded asynchronously

When loading JS, Qiankun would match the corresponding microapplication according to the js loaded, and enable the corresponding sandbox to isolate JS. If js was loaded asynchronously again, the application corresponding to the js loaded asynchronously could not be correctly matched to the microapplication. This will result in failure to open the correct sandbox for isolation, resulting in js global scope contamination.

  • The solution
    • Only multiple asynchronous introductions are loaded firstjsApplications that make all asynchronousjsOnly the sandbox of the application can be matched. After loading, the base will be notified to start loading other normal applications.

6. Set the publicPath on the configuration line and in the local environment to the default path for loading resources

Microapplications cannot use resources with relative paths. Therefore, set the resource loading path to an absolute path and distinguish online and local environments.

const isProd = process.env.NODE_ENV === 'production';
const publicPath = isProd ? ` http://182.48.115.108:8887/${name}/ ` : `http://localhost:${port}`;
 
 // vue.config.js
 module.exports = {
  // ...
  publicPath,
}

// webpack
module.exports = {
  // ...
  assetsPublicPath: publicPath,
}
Copy the code

If you want to access applications properly after local packaging, manually change isProd to false

7. If the online subapplication runs separately, modify the path of the route

Application pack after deployment, in order to let the application run independently, need according to the child application deployment addresses path to set the routing path, such as http://182.48.115.108:8887/vue3-air-app/ application deployment, The path of the sub-application route should be ‘/vue3-air-app’ + ‘/’ instead of ‘/’. For uniformity, the path of the access address should be the name of the application packageName

const packageName = require('.. /.. /package.json').name;
const basePath = '/';
// routes.ts
export default[{// A non-Qiankun environment is an application that runs independently and is an official version. The path of the application must have the name of the application, otherwise it cannot run independently
    path: !(window as any).__POWERED_BY_QIANKUN__ && process.env.NODE_ENV === 'production' ? ` /${packageName}${basePath}`: basePath; , name:'Home'.component: () = > import('@/views/home.vue'),},];Copy the code

8. In addition, several points needing attention are summarized in the access process

  • althoughqiankunsupportjQueryHowever, the old project access of multi-page application is not very friendly, requiring modification of each page and high cost. This kind of old project access is still recommendediframe
  • becauseqiankunThe way is throughHTML-EntryextractingJSFiles andDOMStructured, and actually shared with the main applicationDocumentIf the child application and the main application define the same event at the same time, they will affect each other. For example, useonClickaddEventListener<body>Added a click event,JSThe sandbox doesn’t eliminate it, it’s up to the usual code specification
  • The deployment is a bit cumbersome and requires manual resolution of cross-domain issues
  • invueUse pictures to useRequire (relative/absolute path).defaultGet image path
  • In the applicationjsThrough thefunctionorvarThe statement inwindowThe global variable of theProxyThe sandbox willwindowreplaceProxyThe declared variable cannot be saved in theproxyObject, if you want to use global variables, you can use(function(global){global.obj = {}, global.fn = function() {}}(window))
  • Tripartite JS like map-dependent ones have a lot of uncertainties, such as the introduction ofCesiumJsAnd otherqiankunJs libraries that cannot be perfectly supported, and many static resources introduced into them cannot be replaced by absolute addresses when packaging, so in order to make these tripartite JS libraries can be integrated smoothly, the best way is to put them in the baseindex.htmlLoad in, like thisqiankunThere will be no hijacking of js introduced by three parties and errors will occur.
  • Before switching a subapplication, uninstall the previous subapplication. Otherwise, an error message is displayed
app1 = loadMicroApp();
app1.unmount;
app2 = loadMicroApp();
Copy the code
  • Not compatible with IE, introduce the following dependency solution in the base main.ts
import 'whatwg-fetch';
import 'custom-event-polyfill';
import 'core-js/stable/promise';
import 'core-js/stable/symbol';
import 'core-js/stable/string/starts-with';
import 'core-js/web/url';
Copy the code

8. There may be issues to consider in the future

  • Automatic injection: The process of transformation of each sub-application is actually quite troublesome, but in fact most of the work is standardized process, in consideration of automatic registration of sub-applications through scripts, to achieve automation

conclusion

Actually write down the whole project, the biggest feeling qiankun out-of-box availability is very strong, need to change the project configuration is very few, of course, encountered some pit points must be stepped on to be more clear.

If there are any problems or mistakes in the article, welcome to correct the exchange, thank you!

Easter egg: Bytedance inside Push – Chengdu -2021.05

Bytedance Tiktok social team recruiting!!

Why bytes? As long as NB, finished work can come off work at any time! You can do whatever challenges you like as long as you want! As long as there are opinions, all kinds of feedback group, duty number, onCall at any time to find someone to mention!

Job description

  • Front-end development Engineer – Tik Tok – experience unlimited as long as enough NB
  1. Responsible for the front-end development of Douyin social networking and local life, including the development of core functions and activities on the product side;
  2. Implement and promote product planning and requirements to ensure efficient and high-quality iteration of product functions;
  3. Focus on product process and product experience to create the ultimate user experience;
  4. Participate in technical planning and ensure development experience, r&d efficiency, system performance and safety.

Job requirements

1. Bachelor degree or above, major in computer science, communication and electronic information science, mathematics or related is preferred; 2. Solid computer and front-end skills, good logical understanding and learning ability; 3. Familiar with front-end basic technology and common development framework; 4. Good awareness of interaction design and user experience; 5. Positive and optimistic, strong sense of responsibility, good service awareness and communication and cooperation skills;

Please send your resume to [email protected]

Resume name: RESUME – Douyin Social front – Name – wechat number.pdf

Every person I send resume to provide interview preparation modification Suggestions, advice and resume resume through I would find you push to send code directly, not through will give Suggestions, push the benefit is that every interview link schedule are clear, direct docking people to follow up the progress can be found, waiting for what, opportunity never and others, threw resume can also enter the pool, Increase the chances of your next interview! That’s how I got into bytes, don’t be a coward!!