微前端那些事

简介: 微前端出现在我们的视线的次数越来越多,因为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-微前端的那些事儿


欢迎指出问题🧒



相关文章
|
9月前
|
前端开发 UED
微前端实战
微前端实战
79 2
|
5月前
|
前端开发 JavaScript 架构师
了解微前端,深入前端架构的前世今生
该文章深入探讨了微前端架构的起源、发展及其解决的问题,并详细讲解了微前端在现代Web应用中的实现方式与优势,帮助读者理解微前端的设计理念和技术细节。
|
9月前
|
前端开发 JavaScript 开发者
探索前端领域中的微前端架构
随着前端应用的规模不断增长,传统的单体应用架构已经无法满足需求。本文将深入探讨微前端架构的概念、优势以及实现方式,为前端开发者提供新的思路和解决方案。
|
前端开发 微服务
微前端应用
微前端(Micro-Frontends)是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。
|
前端开发 应用服务中间件 nginx
微前端
微前端,听这名字感觉很超前,其实已经有很多公司都已经用上了该技术,例如我的项目组也用上了,跟后端的微服务很类似,用作解耦。现在的网站开发日渐复杂,通常会有很多独立的模块组成,分成不同的业务组,在之前用的是iframe 进行嵌套。
547 0
微前端
|
前端开发 数据可视化 JavaScript
为什么要用微前端?如何使用乾坤微前端?
为什么要用微前端?如何使用乾坤微前端?
|
存储 缓存 前端开发
理解微前端
我们就从微前端解决了哪些现存前端问题,并在解决这些问题的过程中做出了哪些取舍和权衡。
159 0
|
存储 前端开发 JavaScript
2022 你还不会微前端吗 (下) — 揭秘微前端核心原理(一)
2022 你还不会微前端吗 (下) — 揭秘微前端核心原理
351 0
|
前端开发 JavaScript 数据格式
2022 你还不会微前端吗 (下) — 揭秘微前端核心原理(二)
2022 你还不会微前端吗 (下) — 揭秘微前端核心原理
190 0
|
存储 前端开发 JavaScript
2022 你还不会微前端吗 (下) — 揭秘微前端核心原理(三)
2022 你还不会微前端吗 (下) — 揭秘微前端核心原理
145 0

热门文章

最新文章