mfy

0.4.1 • Public • Published

麦饭(MFY)

简易的无侵入式微前端框架。

不需要对原有应用做任何修改,就可以被融入到新的技术体系中,麦饭的目标,是让开发者以最简单的开发方式,以低廉的成本完成技术迁移。抑或,将你的巨无霸应用得以拆分为多个子应用,让你的开发团队专注于特定的功能应用上,而不受技术栈的影响。虽然小巧,但精悍无比,花5分钟试用它,你一定会爱上麦饭。

除了拥有其他微前端框架几乎所有功能之外,与其他微前端框架的不同点(亮点):

  • 支持子应用嵌套,子应用里面还可以嵌套子应用
  • 支持子应用被挂载的DOM节点被删掉之后又挂载回来,例如使用v-if控制一块区域的隐现
  • 支持路由映射,不需要对子应用进行修改的情况下,把外层的url映射为内层url
  • 动画过渡效果

快速体验小DEMO

安装

npm i mfy

或者直接通过cdn加载麦饭。

<script src="https://unpkg.com/mfy"></script>

快速上手

麦饭总共只有4个接口函数,你可以在5分钟内接入它。

首先,在你需要加载子应用的位置使用<mfy-app>标签占位:

<mfy-app name="some"></mfy-app>

然后注册和启动同名的子应用:

import { importSource, registerMicroApp } from 'mfy'

const app = registerMicroApp({
  name: 'some', // 对应<mfy>标签的name属性值
  source: () => importSource('./apps/some/index.html'),
  autoMount: true, // 自动完成挂载
})

好了,你当前的应用成功加载了子应用。如果没有父子通信的需要,你甚至不需要在子应用中改任何一处。

如果你的子应用足够简单,不需要复杂的通信和路由等,可以直接传入src属性省去registerMicroApp这一步。

<mfy-app name="simple" src="./apps/simple-app.html" mode="shadow"></mfy-app>

此时,name 还是必须的,它保证你的一个页面内的子应用是唯一的,src传入后你就不需要(也不可以)再用registerMicroApp进行注册,同时可以传入mode来选择模式。这种效果有点像直接使用iframe一样简单。

接口

麦饭的四个接口,importSourceregisterMicroApp为子应用的加载和挂载服务,connectScope为父子应用通信服务,registerRouter为路由映射服务。

importSource(relativePath)

导入资源,传入子应用的html入口文件的相对路径,改相对路径是指从当前能被访问到的url到子应用入口文件url地址的相对路径。

importSource('./apps/some/index.html')

在麦饭中,资源加载具有缓存,同一个文件不会加载第二次,而是直接使用缓存,因此你不需要担心统一资源反复加载的问题。一般而言,importSource只会作为registerMicroAppsource参数中使用。

registerMicroApp(options)

注册一个微应用,使html中的<mfy-app>对应name的应用生效。

配置参数如下:

{
  name: string, 对应<mfy-app>的name属性,当前环境中,不允许多次注册同名应用
  source: 资源,只能使用importSource进行导入,直接导入,资源会立即加载,如果接收函数,资源会在bootstrap的时候加载
  mode: iframe|shadow|none, 默认none。子应用的环境隔离类型,默认不做脚本执行环境隔离
  placeholder: html字符串,可选,当资源还没有下载完时,可以用这个字符串渲染,字符串内应该包含样式
  onLoad(): 资源加载好时被调用
  onBootstrap(): 子应用启动时被调用
  onMount(): 子应用被挂载时调用
  onUnmount(): 子应用被卸载时调用
  onDestroy(): 子应用对应的<mfy-app>标签从文档中移除时调用
  onMessage(data): 子应用向当前环境发送消息时调用
  autoBootstrap: 自动启动该子应用
  autoMount: 自动挂载该子应用,包含了autoBootstrap的效果
  hoistCssRules(rule): 哪些样式要被挂载到当前环境的head中实现全局样式,返回样式的字符串文本cssText
  injectCss: string, 注入样式,用以覆盖子应用原本的样式(以style的形式放在子应用head区末尾)
  injectJs: string, 注入JS脚本(以script的形式放在子应用body区末尾)
  viewport: string|stirng[], element selector, 例如 'body > .main-content',表示在子应用加载完之后,将会以 'body > .main-content' 的长宽作为子应用的显示区域,如果是数组的话,第一个作为可视区域依据,其他的都被保留在视口中
}

大多数配置项都是可选的,只有name和source是必须传的。

const app = registerMicroApp(...)

它会返回一个注册好的app对象,该app是和对应的<mfy-app>绑定的。对于一个app而言,它需要被执行两个步骤,才会在界面上展示出来:bootstrap+mount。

app.bootstrap()

bootstrap方法用于启动该app,启动之后,会进行资源加载、环境创建等工作。为了根据实际需要进行这些消耗内存的操作,你可以在不同的时间点上启动app。

app.mount()

mount方法用于将app的内容渲染到界面上,调用之后,你可以通过开发者工具看到<mfy-app>内部发生了变化。

app.unmount()

unmount方法用于将渲染到界面上的内容移除,调用之后,你可以通过开发者工具看到<mfy-app>内部发生了变化。

另外,<mfy-app>在文档中有可能会因为其他程序的操作,比如vue或react的更新操作,会从文档中被移除,一个<mfy-app>标签被移除之后,并不代表这个app被销毁了,这个app仍然存在于内存中,当对应的<mfy-app>重新回到文档中时,它会自动重建环境,并根据销毁前的状态决定是否挂载app。

connectScope(window)

麦饭中,通过scope完成父子应用的通信。一个子应用一定运行在一个由麦饭创建的环境中,这个环境就是scope,一个scope内,可能运行着多个子应用。每一个子应用又包含了一个自己的内部环境scope,在这个scope中,可能又会有新的子应用挂载进来,这样,app+scope就形成了一个树状结构。scope的主要功能,是为父子应用提供通信。

const scope = connectScope(window)

scope.emit({ type: 'event', message: 'ok' }) // 向父应用发送消息
scrope.listen((data) => { // 接收到来自父应用发送的消息
  const { type } = data
  // ...
})
scope.watch(name, (data) => { // 接收到来自子应用发送的消息,name为子应用的名称
  // ..
})
scope.send(name, data) // 向单个子应用发送消息
scope.dispatch({ type: 'event', message: 'gogo' }) // 向所有子应用广播消息(不包含孙应用)
scope.broadcast({ type: 'xx', message: 'oo' }) // 向整个应用树广播消息,自顶向下进行广播

参数window虽然是可选的,但是,我们应该尽可能都传入,因为在沙箱中,window所代表的意思并不完全相同,如果不传,麦饭会自己经过推理得到一个scope,但是这个scope有可能并不正确,特别是在使用type="module"的代码块中。如果你直接传入true代表读取rootScope。

麦饭并不提供全局状态共享的能力,因为应用之间不应该直接共享状态,共享状态导致状态的不可预测性,不利于子应用开发团队专注完成子应用的功能开发。但是,父子通信的能力,实际上提供了传递状态的能力,在必要的时候,可以通过通信机制传递状态。

registerRouter(options)

注册一个路由管理器。通过该方法,你可以把多个子应用放在一个路由管理器下面,用以规定这个子应用在什么路由状态下执行mount/unmount操作。另外,路由系统还提供了路由映射功能,浏览器的url并非直接被子应用识别,子应用识别到的url,来自路由管理器map的结果。我们来看下options都可以进行哪些配置:

{
  routes: [
    {
      app: 对应的app对象
      match: (data) => true|false, 用以决定是否匹配到当前app的函数,当该函数返回true时,app会被mount
      map: (data) => url字符串,用于将外部信息映射为子应用的url,即使子应用是用vue-router等进行路由管理的,也不需要对子应用的路由系统进行修改,我们用map就可以处理好子应用接收到的url信息
      reactive: (data) => {}, 当子应用内部的url被子应用内的程序修改时,该函数被执行,从而可以让外部环境记录子应用的url,从而即使用户直接刷新浏览器,也不会丢失子应用的url
    }
  ]
  autoBoostrap: 是否自动启动路由监听
  transition: fade|slide, 子应用mount/unmount时的过渡动画效果
  onChange(): url发生变化时被被调用
  onEnter(): 有app被mount时被调用
  onLeave(): 有app被unmount时被调用
}

和app的启动一样,你也可以主动调用router.bootstrap()来启动路由监听。

注意点

  • 麦饭不支持跨域拉取资源,因此,请将你的所有应用部署在主应用域名下。
  • 不管是父级应用,还是子应用,麦饭的所有接口函数,必须在脚本顶层执行,不能异步执行上面的任何一个函数(app.bootstrap等对象方法可以异步执行)
  • 不支持子应用通过<script type="module">执行脚本,直接在浏览器中运行的ES脚本目前还不支持创建环境,所以不支持

对于 <scirpt type="module"> 的代码块,麦饭会把它转为常规的代码块,并将原本的 import from 用 import() 代替。并且要求所有模块依赖中没有副作用,也就是不会在模块中修改window等全局变量,而是只导出接口。

License

MIT.

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 0.4.1
    2
    • latest

Version History

Package Sidebar

Install

npm i mfy

Weekly Downloads

2

Version

0.4.1

License

MIT

Unpacked Size

85.3 kB

Total Files

10

Last publish

Collaborators

  • tangshuang