微前端那些事

简介: 微前端出现在我们的视线的次数越来越多,因为to B 的发展越来越迅猛,导致中后台应用需求激增,如何将多项目集合成一个web主体就成为一个问题,当然也有不少童鞋们还会有疑惑🤔,究竟微前端是什么东西呢?

微信截图_20220511174015.png

1.什么是微前端


微前端本质是是一种项目架构方案,是为了解决前端项目太过庞大,导致项目管理维护难、团队协作乱、升级迭代困难、技术栈不统一等等问题,有点类似微服务的概念,是将微服务理念扩展到前端开发的一种应用,讲到这里你可能还是一脸懵逼~,我们接着讲


举个例子:七某云平台


微信截图_20220511174029.png


本质上应该就是一个微前端应用,左侧的菜单就是各个子应用的入口,切换菜单的同时就是在切换子应用,而整个主容器就是一个portal门户(可能包含用户登录机制 、菜单权限获取 、 全局异常处理等)


2.微前端的落地方式


微前端它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用拆分为为多个小型前端应用聚集为一体的应用。


2.1 iFrame


iFrame 是微前端集成的最简单方式之一。可以说iFrame 里的页面是完全独立的,而且iFrame 页面中的静态资源(js、css)都是相互隔离的,互相不干扰,相当于一个独立的环境,具备沙箱隔离,可以让前端应用之间可以相互独立运行


//通过切换url来切换不同业务项目
 <iframe v-show="url" frameborder="0" id="contentIframe"></iframe>
 createFrame(url) {
      const iframe = document.getElementById('contentIframe');
      const deviceWidth = document.body.clientWidth;
      // const deviceHeight = document.body.clientHeight;
      iframe.style.width = `${Number(deviceWidth) - 10}px`;
      iframe.style.height = `${800}px`;
      iframe.src = url;
}


当然iFrame 也有局限性👇:


  • 子项目需调整,需要隐藏自身页面中的导航(公共区域)
  • iFrame嵌入的视图控制难,有局限性
  • 刷新无法保存记录,也就意味着当浏览器刷新状态将消失,后退返回无效
  • iframe 阻塞主页面加载


2.2 路由分发方式


路由分发是指通过路由将不同业务拆分的子项目,结合反向代理的方式实现


路由分发方式也是比较简单的一种实现微前端的方式,将多个子项目聚合成一体,可以通过ngxin来配置不同路由的转发代理,如下


http {
  server {
    listen       80;
    server_name  192.168.0.1
    location /web/monitor {
      proxy_pass http://192.168.0.2/web/monitor;
    }
    location /web/admin {
      proxy_pass http://192.168.0.3/web/admin;
    }
    location / {
      proxy_pass /;
    }
  }
}


通过不同的路由请求,转发到不同的项目域名服务器下,这种方式好处在于团队协作方便、框架无关、项目独立部署维护


当然路由分发也有局限性:


  • web应用之间的复用性差
  • 每个独立的项目之间切换,需要重新加载,容易出现白屏影响用户体验


2.3 Single-SPA


官方号称“一个用于前端微服务化的JavaScript前端解决方案”,single-spa 听起来很高大上,它能兼容各种技术栈,并且在同一个页面中可以使用多种技术框架(React, Vue, Angular等任意技术框架),不用考虑因新的技术框架而去重构旧项目的代码,官方文档🚀


微信截图_20220511174043.png


大概的原理是,首先需要一个主应用(容器应用),需要先注册子应用,然后当url匹配到相应的子应用路由后,将会先请求子应用的资源,然后挂载子应用,同理,当url切换出该子应用路由时,将卸载该应用,以此达到切换子应用的效果,通过子应用生命周期boostrap(获取输出的资源文件) 、 mount、unmount的交替


聊聊Single-SPA 的优点:


  • 各项目独立开发、部署、迭代,互不影响效率高
  • 开发团队可以选择自己的技术并及时更新技术栈。
  • 相互之间的依赖性大大降低
  • 有利于CI/CD,更快的交付产品


由于要把时间留给终极大boss(乾坤qiankun-蚂蚁金服微前端框架),Single-SPA的实践在这里不做大篇幅介绍,有兴趣的童鞋可以看下面几篇文章


前端微服务化解决方案2 - Single-SPA


2.4 qiankun (蚂蚁金服微前端框架)


qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。官方文档🚀


qiankun这名字起得溜啊,本质上就是在上一节提到得Single-SPA上做一些封装,让我们前端开发者用得更上手


  • 主应用下安装 qiankun


yarn add qiankun


  • 如何在主应用中注册子应用


官方文档介绍得是react的方式,而树酱是基于vue开发的,所以这里介绍下vue的方式,本质上就是注册子项目应用(按需加载子项目编译好的静态资源),当子应用加载完之后,浏览器的 url 发生变化时,便会自动触发 qiankun 的路由匹配逻辑,去执行子应用的生命周期函数,以下是具体实现👇,有点长小心头顶


// main.js 入口文件修改
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import axios from 'axios';
import api from "./service";
import ViewUI from 'view-design';
import cacheKeys from '@/const/cacheKey';
import globalMixins from '@/mixin/global';
import Bus from '@/utils/bus';
import 'view-design/dist/styles/iview.css';
import './theme/customTheme.less';
import '@/icons';
Vue.config.productionTip = false;
// 导入乾坤函数
import {
  registerMicroApps,
  runAfterFirstMounted,
  setDefaultMountApp,
  start
} from "qiankun";
// 导入路由监听函数
import { genActiveRule } from "./utils";
// 导入主应用工具类库
import LibraryJs from "./library/js";
// 导入主应用需要下发的emit函数
import * as childEmit from "./utils/childEmit"
// 定义传入子应用的数据
Vue.mixin(globalMixins);
Vue.use(ViewUI);
Vue.use(api);
Vue.use(Bus);
Vue.prototype.$axios = axios;
Vue.prototype.$cacheKeys = cacheKeys;
Vue.config.productionTip = false;
// 定义传入子应用的数据
let msg = {
  data: store,         // 从主应用仓库读出的数据
  // components: LibraryUi,       // 从主应用读出的组件库
  utils: LibraryJs,            // 从主应用读出的工具类库
  emitFnc: childEmit,           // 从主应用下发emit函数来收集子应用反馈
  prototype: [
    {name: '$axios', value: axios },
    {name: 'isQiankun', value: true },//是否qiankun启用
  ]
};
// 主应用渲染函数
let app = null;
function render({ appContent, loading } = {}) {
  if (!app) {
    app = new Vue({
      el: "#container",
      router,
      store,
      data() {
        return {
          content: appContent,
          loading
        };
      },
      render(h) {
        return h(App, {
          props: {
            content: this.content,
            loading: this.loading
          }
        });
      }
    });
  } else {
    app.content = appContent;
    app.loading = loading;
  }
  window.vue = app;
};
render();
//注册子应用
registerMicroApps(
  [
    {
      name: "monitor",
      entry: "http://10.0.0.110:8081/",
      render,
      activeRule: genActiveRule("/monitor"),
      props: msg
    },
    {
      name: "portalAdmin",
      entry: "http://183.62.46.202:8082/",
      render,
      activeRule: genActiveRule("/admin"),
      props: msg
    }
  ],
  {
    beforeLoad: [
      app => {
        console.log("before load", app);
      }
    ],
    beforeMount: [
      app => {
        console.log("before mount", app);
      }
    ],
    afterUnmount: [
      app => {
        console.log("after unload", app);
      }
    ]
  },
);
// 设置默认子应用
setDefaultMountApp("/portal");
// 第一个子应用加载完毕回调
runAfterFirstMounted(() => {
  // console.log(app)
});
// 启动微服务
start({ prefetch: true });
/* new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app"); */


  • 子应用适配


子应用不需要额外安装任何其他依赖即可接入 qiankun 主应用,只需向主应用暴露相应的生命周期钩子


import Vue from 'vue';
import VueRouter from 'vue-router';
import routes from './router';
import './public-path';
// 声明变量管理vue及路由实例
let router = null;
let instance = null;
// 导出子应用生命周期 挂载前
export async function bootstrap(props = {}) {
  Vue.prototype.isQiankun = props.isQiankun;
}
// 导出子应用生命周期 挂载前 挂载后
export async function mount({data = {}} = {}) {
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/portal' : '/',
    mode: "history",
    routes
  });
  window.vue = instance = new Vue({
    router,
    store,
    render: h => h(App, {props: data})
  }).$mount("#app");
};
// 导出子应用生命周期 挂载前 卸载后
export async function unmount() {
  instance.$destroy();
  instance = null;
  router = null;
}
// 单独开发环境
window.__POWERED_BY_QIANKUN__ || mount();


子应用挂载到主应用,通过props


还需main.js 中引入 public-path.js 文件


// public-path.js 
if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}


配置好生命周期钩子函数后,为了让主应用能获取子应用暴露的资源文件,子应用的打包工具需要增加如下配置:


// vue.config.js
module.exports = {
  resolve: {
            alias: {
                '@': resolve('src'),
            },
    },
   configureWebpack: {
    output: {
      library: `${name}-[name]`,
      filename: '[name].js',
      libraryTarget: 'umd',
      globalObject: 'this',
    },
  },
}


以上即完成了子父应用的微前端适配,过程中会有可能会遇到一些奇奇怪怪的问题,可以查看官方的见问题 以及 github 上面的 issue


完成以上步骤,当你想部署到测试环境或者生产环境的时候,还需要配置nginx。

首先先聊聊子应用的nginx配置


events {
    worker_connections  1024;
}
http{
    server {
        listen       80;
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Headers X-Requested-With;
        add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
        location / {
            try_files $uri $uri/ /index.html;
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
    }
}


  • vue路由模式 :子项目的路由模式为history,因此需要再nginx配置try_files $uri $uri/ /index.html; 否则当你刷新路由时会报404 vue 官方文档


  • 资源跨域问题:需要设置允许访问的来源,如果没有配置,主应用将因为跨域无法正常获取子应用的资源(js、css)


配置完子应用,主应用的ngnix也不能漏


http{
    server {
        listen       80;
        location / {
            try_files $uri $uri/ /index.html;
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
        location /monitor {
           try_files $uri $uri/ /index.html;
           proxy_pass http://10.0.0.110:8081;
        }
        location /admin {
            try_files $uri $uri/ /index.html;
            proxy_pass http://183.62.46.202:8082;
         }
    }
}


大功告成


微信截图_20220511174101.png


3 总结


3.1 一些优质的微前端文章分享


可能是你见过最完善的微前端解决方案


微前端架构选型指南


每日优鲜供应链前端团队微前端改造


alili.tech 微前端


Micro-frontend Architecture in Action-微前端的那些事儿


欢迎指出问题🧒



相关文章
|
JavaScript 前端开发
JS require 与 import 的区别
JS require 与 import 的区别
660 1
|
4月前
|
数据可视化 前端开发 数据管理
什么是低代码?一文看懂:低代码技术的发展历程及技术架构
低代码开发平台通过可视化界面与组件化设计,大幅降低编程门槛,使开发者无需大量编码即可快速构建应用。它具备可视化开发、预制组件、低技术门槛及全流程支持等核心特征,适用于业务流程自动化、数据管理、客户关系管理等多种场景。自萌芽期至今,低代码不断演进,成为企业数字化转型的重要工具,显著提升开发效率、降低成本,并推动全民开发者时代的到来。
835 0
什么是低代码?一文看懂:低代码技术的发展历程及技术架构
|
存储 前端开发 UED
React 面包屑组件 Breadcrumb 详解
面包屑导航是现代Web应用中常见的UI元素,帮助用户了解当前位置并快速返回上级页面。本文介绍如何使用React构建面包屑组件,涵盖基本概念、实现方法及常见问题。通过函数式组件和钩子,结合React Router动态生成路径,处理嵌套路由,并确保可访问性。示例代码展示了静态和动态面包屑的实现,帮助开发者提升用户体验。
675 73
|
消息中间件 人工智能 Kubernetes
解密开源Serverless容器框架:事件驱动篇
Knative是一款基于Kubernetes的开源Serverless框架,提供了云原生、跨平台的Serverless编排标准。作为Serverless中必不可少的事件驱动能力,Knative Eventing提供了云原生的事件驱动能力。
|
JavaScript 前端开发 API
JavaScript全屏,监听页面是否全屏
JavaScript全屏,监听页面是否全屏
321 0
|
前端开发 JavaScript 开发者
利用代码分割优化前端性能:高级技巧与实践
【10月更文挑战第2天】在现代Web开发中,代码分割是优化前端性能的关键技术,可显著减少页面加载时间。本文详细探讨了代码分割的基本原理及其实现方法,包括自动与手动分割、预加载与预取、动态导入及按需加载CSS等高级技巧,旨在帮助开发者提升Web应用性能,改善用户体验。
|
前端开发 JavaScript Java
【Layui】选项卡Tab:完美实现网页内容分类与导航
Layui选项卡是一种基于Layui框架的前端组件,用于创建多个选项卡并在其之间进行切换。Layui是一个轻量级、易用、灵活的前端UI框架,旨在简化前端开发过程。使用Layui选项卡,你可以在页面中创建多个选项卡标签,每个标签对应一个内容区域。用户可以点击选项卡标签来切换显示相应的内容区域。这种方式常用于页面的分组显示或切换不同的功能模块。Layui选项卡提供了丰富的配置选项,包括标签样式、选项卡切换的触发事件、内容区域的布局方式等。它还支持动态添加和删除选项卡,以及自定义选项卡的样式和功能。
Vue3面包屑(Breadcrumb)
该Breadcrumb组件允许自定义设置多个属性,包括路由数组、面包屑类名和样式、文本最大显示宽度、分隔符及样式、以及目标URL的打开方式。通过这些配置项,可以轻松实现不同样式的面包屑导航。组件支持点击跳转,并且能够处理带查询参数的路径。在线预览展示了其丰富的定制功能。可通过引入并在页面中使用该组件来快速构建导航结构。
491 1
Vue3面包屑(Breadcrumb)
|
JavaScript 前端开发
Vue3动态面包屑的实现
Vue3动态面包屑的实现
555 0
|
JSON JavaScript API
wangEditor 富文本详解(下)
wangEditor 富文本详解(下)
1762 0

热门文章

最新文章