从零到一实现企业级微前端框架,保姆级教学

本文详尽地介绍了如何从零实现一个微前端框架,涵盖了路由劫持、应用注册、生命周期管理、资源加载和JS沙箱等核心功能。作者通过实例讲解了微前端的不同实现方案,并提供了完整的代码仓库供读者参考。文章适合对微前端感兴趣的开发者深入理解其原理和实现。
摘要由CSDN通过智能技术生成

前言

这篇文章笔者足足肝了一周多,多次斟酌修改内容,力求最大程度帮助读者造出一个微前端框架,搞懂原理。觉得内容不错的读者点个赞支持下。

微前端是目前比较热门的一种技术架构,挺多读者私底下问我其中的原理。为了讲清楚原理,我会带着大家从零开始实现一个微前端框架,其中包含了以下功能:

•如何进行路由劫持•如何渲染子应用•如何实现 JS 沙箱及样式隔离•提升体验性的功能

另外在实现的过程中,笔者还会聊聊目前有哪些技术方案可以去实现微前端以及做以上功能的时候有哪些实现方式。

这里是本次文章的最终产出物仓库地址:toy-micro[1]

微前端实现方案

微前端的实现方案有挺多,比如说:

1.qiankun[2],icestark[3] 自己实现 JS 及样式隔离2.emp[4],Webpack 5 Module Federation(联邦模块)方案3.iframe 、WebComponent 等方案,浏览器原生隔离,但存在一些问题

更新:这里之前有一个错误,笔者错把 icestark 的技术方案说成了 iframe 的。

但是这么多实现方案解决的场景问题还是分为两类:

•单实例:当前页面只存在一个子应用,一般使用 qiankun 就行•多实例:当前页面存在多个子应用,可以使用浏览器原生隔离方案,比如 iframe 或者 WebComponent 这些

当然了,并不是说单实例只能用 qiankun,浏览器原生隔离方案也是可行的,只要你接受它们带来的不足就行:

iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但他的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。

上述内容摘自Why Not Iframe[5]

本文的实现方案和 qiankun 一致,但是其中涉及到的功能及原理方面的东西都是通用的,你换个实现方案也需要这些。

前置工作

在正式开始之前,我们需要搭建一下开发环境,这边大家可以任意选择主 / 子应用的技术栈,比如说主应用用 React,子应用用 Vue,自行选择即可。每个应用用对应的脚手架工具初始化项目就行,这边就不带着大家初始化项目了。记得如果是 React 项目的话,需要另外再执行一次 yarn eject

推荐大家直接使用笔者仓库[6]里的 example 文件夹,该配置的都配置好了,大家只需要安心跟着笔者一步步做微前端就行。 例子中主应用为 React,子应用为 Vue,最终我们生成的目录结构大致如下:

截屏2021-08-30下午10.15.01

正文

在阅读正文前,我假定各位读者已经使用过微前端框架并了解其中的概念,比如说知晓主应用是负责整体布局以及子应用的配置及注册这类内容。如果还未使用过,推荐各位简略阅读下任一微前端框架使用文档。

应用注册

在有了主应用之后,我们需要先在主应用中注册子应用的信息,内容包含以下几块:

•name:子应用名词•entry:子应用的资源入口•container:主应用渲染子应用的节点•activeRule:在哪些路由下渲染该子应用

其实这些信息和我们在项目中注册路由很像,entry 可以看做需要渲染的组件,container 可以看做路由渲染的节点,activeRule 可以看做如何匹配路由的规则。

接下来我们先来实现这个注册子应用的函数:

// src/types.ts
export interface IAppInfo {
  name: string;
  entry: string;
  container: string;
  activeRule: string;
}
// src/start.ts
export const registerMicroApps = (appList: IAppInfo[]) => {
  setAppList(appList);
};
// src/appList/index.ts
let appList: IAppInfo[] = [];
export const setAppList = (list: IAppInfo[]) => {
  appList = list;
};
export const getAppList = () => {
  return appList;
};

上述实现很简单,就只需要将用户传入的 appList 保存起来即可。

路由劫持

在有了子应用列表以后,我们需要启动微前端以便渲染相应的子应用,也就是需要判断路由来渲染相应的应用。但是在进行下一步前,我们需要先考虑一个问题:如何监听路由的变化来判断渲染哪个子应用?

对于非 SPA(单页应用) 架构的项目来说,这个完全不是什么问题,因为我们只需要在启动微前端的时候判断下当前 URL 并渲染应用即可;但是在 SPA 架构下,路由变化是不会引发页面刷新的,因此我们需要一个方式知晓路由的变化,从而判断是否需要切换子应用或者什么事都不干。

如果你了解过 Router 库原理的话,应该马上能想到解决方案。如果你并不了解的话,可以先自行阅读笔者之前的文章[7]

为了照顾不了解的读者,笔者这里先简略的聊一下路由原理。

目前单页应用使用路由的方式分为两种:

1.hash 模式,也就是 URL 中携带 #2.histroy 模式,也就是常见的 URL 格式了

以下笔者会用两张图例展示这两种模式分别会涉及到哪些事件及 API:

img
img

从上述图中我们可以发现,路由变化会涉及到两个事件:

popstatehashchange

因此这两个事件我们肯定是需要去监听的。除此之外,调用 pushState 以及 replaceState 也会造成路由变化,但不会触发事件,因此我们还需要去重写这两个函数。

知道了该监听什么事件以及重写什么函数之后,接下来我们就来实现代码:

// src/route/index.ts
// 保存原有方法
const originalPush = window.history.pushState;
const originalReplace = window.history.replaceState;
export const hijackRoute = () => {
  // 重写方法
  window.history.pushState = (...args) => {
    // 调用原有方法
    originalPush.apply(window.history, args);
    // URL 改变逻辑,实际就是如何处理子应用
    // ...
  };
  window.history.replaceState = (...args) => {
    originalReplace.apply(window.history, args);
    // URL 改变逻辑
    // ...
  };
  // 监听事件,触发 URL 改变逻辑
  window.addEventListener("hashchange", () => {});
  window.addEventListener("popstate", () => {});
  // 重写
  window.addEventListener = hijackEventListener(window.addEventListener);
  window.removeEventListener = hijackEventListener(window.removeEventListener);
};
const capturedListeners: Record<EventType, Function[]> = {
  hashchange: [],
  popstate: [],
};
const hasListeners = (name: EventType, fn: Function) => {
  return capturedListeners[name].filter((listener) => listener === fn).length;
};
const hijackEventListener = (func: Function): any => {
  return function (name: string, fn: Function) {
    // 如果是以下事件,保存回调函数
    if (name === "hashchange" || name === "popstate") {
      if (!hasListeners(name, fn)) {
        capturedListeners[name].push(fn);
        return;
      } else {
        capturedListeners[name] = capturedListeners[name].filter(
          (listener) => listener !== fn
        );
      }
    }
    return func.apply(window, arguments);
  };
};
// 后续渲染子应用后使用,用于执行之前保存的回调函数
export function callCapturedListeners() {
  if (historyEvent) {
    Object.keys(capturedListeners).forEach((eventName) => {
    
建立一个BBS论坛需要考虑以下几个方面: 1. 确定论坛类型和功能 2. 设计数据库结构 3. 编写前端界面 4. 编写后端逻辑 5. 部署和维护 下面是一个从零开始基于Python开发BBS论坛的保姆教学: 1. 确定论坛类型和功能 首先要确定你要创建的论坛类型和功能。是一个简单的问答论坛,还是一个社区论坛,还是一个资讯论坛?你需要确定你要实现的功能,例如用户注册、登录、发帖、回复、私信、搜索等等。 2. 设计数据库结构 我们需要设计数据库来存储用户信息、论坛帖子、回复等数据。可以使用MySQL、SQLite等关系型数据库。设计数据库结构时需要考虑到数据的关联性和一致性。 3. 编写前端界面 接下来,我们需要设计前端界面,包括网页布局、样式和交互效果。可以使用HTML、CSS和JavaScript等技术来实现。建议使用Bootstrap等前端框架来快速搭建界面。 4. 编写后端逻辑 编写后端逻辑需要使用Python开发框架,比如Django、Flask等。后端逻辑包括用户认证、数据读写、业务逻辑等。我们需要根据前端界面设计API接口,通过Python编写后端逻辑来实现这些功能。 5. 部署和维护 最后,我们需要将BBS论坛部署到服务器上,并对其进行维护。可以选择使用云服务器,比如AWS、阿里云等。并且需要定期更新代码、备份数据和优化性能等。 总结: 以上就是基于Python开发BBS论坛的保姆教学。需要注意的是,BBS论坛涉及到用户隐私和数据安全等问题,需要谨慎处理。另外,由于开发BBS论坛需要包括前端、后端和数据库等多个方面的知识,所以建议有一定的编程经验的人才尝试开发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值