微前端模式下子应用最常访问页面最佳实现

2021/8/2 13:05:41

本文主要是介绍微前端模式下子应用最常访问页面最佳实现,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、前言

最近一直在做tob项目,一条业务线多个tob工程

之前有文章写过实现微前端 👉tob系统微前端实践总结

也写过多工程之间都存在的公共模块如何处理 👉vue多工程间公共模块处理最佳实践

最近遇到一个比较有意思、且通用的需求🧐 本着总结为最佳实践的初心,写下本篇文章,欢迎大家讨论🤩

二、项目需求

在微前端的基座系统“工作台“页面实现一个”最常访问页面“功能,根据用户使用各子应用页面的频率,记录出该用户最常访问的子应用top4页面🤖

(注意:top4页面属于各个不同的子应用页面,基座系统的页面不在记录范围内,因为基座系统的入口相较子应用页面入口浅,可能记录在最常访问页面模块,同时工作台上有其他快捷入口)

问题关键点:

  1. 需要至少记录页面的url地址,及页面对应的菜单名称

  • 我们的系统因为之前做过统一面包屑处理,所以每个页面router-mate路由元信息里面都有对应菜单名称

  • 如果没有的话可以加上,顺便把统一面包屑给实现了,或者加判断过滤掉没有菜单名称的页面(兜底),即没有菜单名称的页面不再记录范围内

  • url地址:好说,有页面就有路由,只是需要注意,有些页面是动态路由、带有参数,所以需要记录的是完成的url地址,比如/lego/#/cms-page-manage/template-instance/view?pageId=555&authCode=lego_page_555

  • 菜单名称:一般在一级、二级菜单上的页面都会有对应的菜单名称,而非菜单上的页面,比如详情页等,除非在router-mate路由元信息里面有对应菜单名称

服务端记录还是前端记录,考虑到SPA时代、服务端记录实在太重,前端用localstorage记录实现

  • 需要记录页面的访问次数,根据次数取出最常访问top4

  • 记录页面最后一次访问的时间戳,比如访问次数最多的6个页面访问的次数依次是23(页面1),17(页面2),14(页面3),9(页面4),9(页面5),9(页面6);那么top4的第四个页是页面几,应该根据时间戳来,取时间戳大的那个页面(也就是最近访问的那个页面)

在哪个时刻将页面访问记录存到localstorage也很关键,是否可以不侵入子应用,在基座系统就实现?下面技术方案实现具体说明

三、技术实现

我们的技术栈都是vue,所以下面的实现方案都是基于vue,但是思路是通用的~
在全局前置守卫,打印子应用路由信息

import VueRouter from "vue-router";

const router = new VueRouter({ ... })


router.beforeEach((to, from, next) => {

    console.log(to)

}


子应用打印出如下:http://img3.sycdn.imooc.com/6107676c0001c79008740978.jpg基座系统打印出如下:http://img1.sycdn.imooc.com/6107676d00015d9513960326.jpg可以看到没有name,没有meta,针对问题关键点1, 在基座系统通过路由可以拿到url,但是拿不到菜单名称

为什么没有name,没有mate?
基座系统的路由跟子应用的路由不是同一个实例。因为在基座系统中,子应用的路由是/*,不清楚这块的指路tob系统微前端实践总结、qiankun

方案一:微前端提供事件,在子应用的全局前置守卫触发事件存localstorage

基座系统:注册全局事件中心,监听用户打开子应用页面行为

import Event from 'eventemitter3';

import { setMenuUrl } from "@/utils/menu-url";

const eventCenter = new Event();


// 监听用户打开子应用页面行为

window.eventCenter.on('GET_SUPAPP_MENU', ({ path, name }) => {

     // 存localstorage操作

     setMenuUrl(name, path)

 })


子应用系统:在全局前置守卫触发用户打开页面事件

import VueRouter from "vue-router";

const router = new VueRouter({ ... })


router.beforeEach((to, from, next) => {

     if (window.__POWERED_BY_MICRO__) { // 微前端模式下

         // 触发用户打开页面事件

         window.eventCenter.emit("GET_SUPAPP_MENU", {path: `/${process.env.VUE_APP_NAME}#${to.fullPath}`, name: to.meta.menu.title});

     }

}


缺点:侵入子应用,需要对n个子应用都做处理

那有没有可以不侵入子应用去实现呢,见方案二

方案二:在基座系统的子应用容器组件中通过获取document.title存localstorage

其实就是想,还有没有办法可以拿到页面的菜单名称,document.title是可以的,也就是浏览器的标题,如下图http://img3.sycdn.imooc.com/6107679b0001c98006260146.jpg
上面说了,我们的系统每个页面都有名称,document.title也都根据页面名称设置了,没有设置的话,可以设置上,这样更规范~

基座系统是可以拿到子系统的url的,所以可以在基座系统的子应用容器组件(可参考:tob系统微前端实践总结)中去处理,如下代码

// 子应用容器.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

      };

      // 监听dom节点 titie变化

      const observer = new MutationObserver(callback);

      observer.observe(targetNode, config);

      vm.handleRouteChange.apply(vm, [to]);

    });

  },

   methods: {

    // 监听路由变化

    handleRouteChange(val) {

      if(this.documentTitle != process.env.VUE_APP_INDEX_TITLE && val.fullPath.split("/").length>3){

          // 存localstorage操作

          setMenuUrl(this.documentTitle, val.fullPath)

      }

    }

   }

}

</scripe>


针对问题关键点2,如何存localstorage

\\ src/utils/menu-url.js

import { storage } from './storage'


export const setMenuUrl = (name, url) => {

  if(!storage.getItem('menuUrl')){

    // 默认常用访问模块

    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))

}


浏览器localstorage截图 

http://img4.sycdn.imooc.com/610767ea0001ed0913620724.jpg


作者:jjjona0215
链接:https://juejin.cn/post/6991396113576099870
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。




这篇关于微前端模式下子应用最常访问页面最佳实现的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程