One, foreword

Recently, I have been working on TOB projects, including multiple TOB projects in one line of business

Before the article has written the realization of micro front-end 👉 TOB system micro front-end practice summary

Also write too many projects exist between the public module how to deal with 👉 vue multi-project public module handling best practices

One of the most interesting and common requirements I recently encountered was 🧐. In the spirit of summarizing as best practices, I wrote this article and welcome your discussion at 🤩

Ii. Project requirements

In the “workbench” page of the base system of the micro front end, a “most frequently visited page” function is implemented. According to the frequency of the user using each sub-application page, the top4 pages of the most frequently visited sub-application by the user are recorded 🤖

(Note: Top4 pages belong to different sub-application pages, and the page of the base system is not within the scope of recording, because the entrance of the base system is shallower than that of the sub-application page, which may be recorded in the most frequently visited page module, and there are other shortcuts on the workbench)

Key points:

  1. Need to record at least the pageThe url address, and the corresponding pageThe name of the menu
    • The url addressNote that some pages are dynamically routed, with parameters, so you need to record the completed URL address, such as/lego/#/cms-page-manage/template-instance/view? pageId=555&authCode=lego_page_555
    • The name of the menu: Usually the page on the first and second level menu will have the corresponding menu name, not the page on the menu, for exampleDetails pageUnless there is a menu name in the router-mate routing meta information
      • In our system, there is a corresponding menu name in the router-mate routing meta information of each page because of the unified breadcrumb processing
      • If not, you can add, incidentally, the unified breadcrumb to achieve, or add a judgment filter out the page without menu name (bottom), that is, no menu name pages no longer record scope
  2. Service end record or front-end record, considering the SPA era, service end record is too heavy, front-end useLocalstorage record implementation
    • You need to record the number of page visits, and obtain the top4 most frequently accessed pages based on the number of visits
    • Record the timestamp of the last visit of the page. For example, the number of visits of the 6 most visited pages is 23(page 1), 17(page 2), 14(page 3), 9(page 4), 9(page 5), 9(page 6). So the fourth page of the top4 is the page number, which should be based on the timestamp, the page with the largest timestamp (i.e. the most recently visited page).
  3. It is also important to save the page access record to localStorage at what time. Can you do it on the base system without invading the child application? The following technical scheme implementation is detailed

Third, technical implementation

Our technology stack is vUE, so the following implementation is based on VUE, but the idea is universal ~ global front-guard, printing child application routing information

import VueRouter from "vue-router";
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) = > {
    console.log(to)
}
Copy the code

The child application prints the following: The base system prints the following: You can see there’s no name, no meta, forKey Point 1In the base system, the url can be retrieved by routing, but the menu name cannot be retrieved

Why no name, no mate? The routing of the base system is not the same instance as the routing of the child application. Because in the base system, the sub-application routing is /*, it is not clear that this piece of guiding TOB system micro front end practice summary, Qiankun

Scenario 1: the micro front end provides events that trigger events to localstorage in the child application’s global front guard

Pedestal system: Registers global event center, listens for user to open child application page behavior

import Event from 'eventemitter3';
import { setMenuUrl } from "@/utils/menu-url";
const eventCenter = new Event();

// Listen for the user to open the child application page
window.eventCenter.on('GET_SUPAPP_MENU'.({ path, name }) = > {
     // Save localstorage operation
     setMenuUrl(name, path)
 })
Copy the code

Child application system: triggers the user to open the page event in the global front guard

import VueRouter from "vue-router";
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) = > {
     if (window.__POWERED_BY_MICRO__) { // In micro front-end mode
         // Triggers the user to open the page
         window.eventCenter.emit("GET_SUPAPP_MENU", {path: ` /${process.env.VUE_APP_NAME}#${to.fullPath}`.name: to.meta.menu.title}); }}Copy the code

Disadvantages: Invade sub application, need to do n sub application processing

That can not invade the child application to achieve it, see plan two

Scheme 2: Store localstorage in the sub-application container component of the base system by obtaining Document. title

In fact, there is no way to get the page menu name, document.title is ok, that is, the browser title, as shown below

As mentioned above, our system has a name for each page, document.title is also set according to the page name, if not set, you can set on, so that more standard ~

The url of the subsystem is available for the base system, so it can be handled in the sub-application container component of the base system (see: Summary of toB System Micro front-end practice), as shown in the following code

Vue import {setMenuUrl} from "@/utils/menu-url";<script>
export default {
  data() {
    return {
      documentTitle: document.title
    };
  },
  watch: {
    $route(val) {
      this.handleRouteChange(val); }},beforeRouteEnter(to, from, next) {
    next((vm) = > {
      const targetNode = document.getElementsByTagName("title") [0];
      const config = { attributes: true.childList: true.subtree: true };
      const callback = function () {
        vm.documentTitle = document.title
      };
      // Listen for dom node titie changes
      const observer = new MutationObserver(callback);
      observer.observe(targetNode, config);
      vm.handleRouteChange.apply(vm, [to]);
    });
  },
   methods: {
    // Listen for route changes
    handleRouteChange(val) {
      if(this.documentTitle ! = process.env.VUE_APP_INDEX_TITLE && val.fullPath.split("/").length>3) {// Save localstorage operation
          setMenuUrl(this.documentTitle, val.fullPath)
      }
    }
   }
}
</scripe>
Copy the code

For key point 2, how to save localstorage

\\ src/utils/menu-url.js
import { storage } from './storage'

export const setMenuUrl = (name, url) = > {
  if(! storage.getItem('menuUrl')) {// Common access modules by default
    const initMenuUrl = {
      url: {
        name: "xxx".count: 1.time: new Date().getTime()
      }
    }
    storage.setItem('menuUrl'.JSON.stringify(initMenuUrl))
  }
  const menuUrl = JSON.parse(storage.getItem('menuUrl'))
  if(menuUrl[url]){
    menuUrl[url].count++,
    menuUrl[url].time = new Date().getTime()
  } else {
    menuUrl[url] = {
      name,
      count: 1.time: new Date().getTime()
    }
  }
  storage.setItem('menuUrl'.JSON.stringify(menuUrl))
}
Copy the code

Screenshot of localStorage browser